diff --git a/engine/createDbFromSchema.js b/engine/createDbFromSchema.js new file mode 100644 index 0000000..52ebf87 --- /dev/null +++ b/engine/createDbFromSchema.js @@ -0,0 +1,163 @@ +require("dotenv").config({ path: "./../.env" }); + +/** ********************************************** */ + +const dbHandler = require("../functions/backend/dbHandler"); +const noDatabaseDbHandler = require("../functions/backend/noDatabaseDbHandler"); +const varDatabaseDbHandler = require("../functions/backend/varDatabaseDbHandler"); +const createTable = require("./utils/createTable"); +const updateTable = require("./utils/updateTable"); + +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ + +async function createDbFromSchema({ path }) { + /** + * Grab Schema + * + * @description Grab Schema + */ + const dbSchema = require(path); + + for (let i = 0; i < dbSchema.length; i++) { + const database = dbSchema[i]; + const { dbFullName, tables } = database; + + /** ********************************************** */ + + // const showDatabases = await noDatabaseDbHandler(`SHOW DATABASES`); + const dbCheck = await noDatabaseDbHandler(`SELECT SCHEMA_NAME AS dbFullName FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '${dbFullName}'`); + + if (dbCheck && dbCheck[0]?.dbFullName) { + // Database Exists + } else { + const newDatabase = await noDatabaseDbHandler(`CREATE DATABASE IF NOT EXISTS \`${dbFullName}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_bin`); + } + + /** ********************************************** */ + /** ********************************************** */ + /** ********************************************** */ + + /** + * Handle Individual Tables + * + * @description Handle Individual Tables + */ + const allTables = await noDatabaseDbHandler(`SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='${dbFullName}'`); + + let tableDropped; + + for (let tb = 0; tb < allTables.length; tb++) { + const { TABLE_NAME } = allTables[tb]; + + if (!tables.filter((_table) => _table.tableName === TABLE_NAME)[0]) { + const oldTableFilteredArray = tables.filter((_table) => _table.tableNameOld && _table.tableNameOld === TABLE_NAME); + + if (oldTableFilteredArray && oldTableFilteredArray[0]) { + console.log("Renaming Table"); + await varDatabaseDbHandler({ + queryString: `RENAME TABLE \`${oldTableFilteredArray[0].tableNameOld}\` TO \`${oldTableFilteredArray[0].tableName}\``, + database: dbFullName, + }); + } else { + console.log(`Dropping Table from ${dbFullName}`); + await varDatabaseDbHandler({ + queryString: `DROP TABLE \`${TABLE_NAME}\``, + database: dbFullName, + }); + + tableDropped = true; + } + } + } + + /** ********************************************** */ + /** ********************************************** */ + /** ********************************************** */ + + for (let t = 0; t < tables.length; t++) { + const table = tables[t]; + + if (tableDropped) continue; + + const { tableName, fields, indexes } = table; + + const tableCheck = await varDatabaseDbHandler({ + queryString: ` + SELECT EXISTS ( + SELECT + TABLE_NAME + FROM + information_schema.TABLES + WHERE + TABLE_SCHEMA = '${dbFullName}' AND + TABLE_NAME = '${table.tableName}' + ) AS tableExists`, + database: dbFullName, + }); + + /** ********************************************** */ + + if (tableCheck && tableCheck[0]?.tableExists > 0) { + // Update Existing Table + const updateExistingTable = await updateTable({ + dbFullName: dbFullName, + tableName: tableName, + tableInfoArray: fields, + varDatabaseDbHandler, + userId, + dbSchema, + tableIndexes: indexes, + }); + + if (table.childrenTables && table.childrenTables[0]) { + for (let ch = 0; ch < table.childrenTables.length; ch++) { + const childTable = table.childrenTables[ch]; + + const updateExistingChildTable = await updateTable({ + dbFullName: childTable.dbNameFull, + tableName: childTable.tableName, + tableInfoArray: fields, + varDatabaseDbHandler, + userId, + dbSchema, + tableIndexes: indexes, + clone: true, + }); + + console.log(updateExistingChildTable); + } + } + + /** ********************************************** */ + } else { + /** ********************************************** */ + + // Create New Table + const createNewTable = await createTable({ tableName: tableName, tableInfoArray: fields, varDatabaseDbHandler, dbFullName: dbFullName, dbSchema }); + } + + /** ********************************************** */ + } + } + + process.exit(); + + /** ********************************************** */ + /** ********************************************** */ + /** ********************************************** */ +} + +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ + +const path = process.argv[process.argv.indexOf("--path") + 1]; + +createDbFromSchema({ path }); diff --git a/engine/index.js b/engine/index.js new file mode 100644 index 0000000..38155a5 --- /dev/null +++ b/engine/index.js @@ -0,0 +1,56 @@ +/** + * ============================================================================== + * Imports + * ============================================================================== + */ +const imageInputFileToBase64 = require("./media/imageInputFileToBase64"); +const imageInputToBase64 = require("./media/imageInputToBase64"); +const inputFileToBase64 = require("./media/inputFileToBase64"); +const getAccessToken = require("./auth/google/getAccessToken"); +const logout = require("./auth/logout"); + +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ + +/** + * ============================================================================== + * Media Functions Object + * ============================================================================== + */ +const media = { + imageInputToBase64: imageInputToBase64, + imageInputFileToBase64: imageInputFileToBase64, + inputFileToBase64: inputFileToBase64, +}; + +/** + * ============================================================================== + * Media Functions Object + * ============================================================================== + */ +const auth = { + google: { + getAccessToken: getAccessToken, + }, + logout: logout, +}; + +/** + * ============================================================================== + * Main Export + * ============================================================================== + */ +const dsqlEngine = { + media: media, + auth: auth, +}; + +module.exports = dsqlEngine; + +/** ********************************************** */ +/** ********************************************** */ +/** ********************************************** */ diff --git a/index.js b/index.js index 9d37d5b..d700e66 100644 --- a/index.js +++ b/index.js @@ -14,6 +14,7 @@ const userAuth = require("./users/user-auth"); const reAuthUser = require("./users/reauth-user"); const getUser = require("./users/get-user"); const loginWithGoogle = require("./users/social/google-auth"); +const sanitizeSql = require("./utils/functions/sanitizeSql"); /** ****************************************************************************** */ /** ****************************************************************************** */ @@ -59,6 +60,7 @@ const datasquirel = { post: post, media: media, user: user, + sanitizeSql: sanitizeSql, }; module.exports = datasquirel; diff --git a/package.json b/package.json index 76ec7e5..a378372 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "datasquirel", - "version": "1.1.54", + "version": "1.1.55", "description": "Cloud-based SQL data management tool", "main": "index.js", "scripts": { diff --git a/types/user.td.js b/types/user.td.js new file mode 100644 index 0000000..74ebfa9 --- /dev/null +++ b/types/user.td.js @@ -0,0 +1,12 @@ +/** + * @typedef {object} DATASQUIREL_LoggedInUser + * @property {number} id - user id (number) + * @property {string} first_name - User First Name + * @property {string} last_name - User Last Name + * @property {string} image - User Full Image + * @property {string} image_thumbnail - User Image Thumbnail + * @property {string} [social_id] - User Social id if available + * @property {number} social_login - 0 or 1 => is this user a social user(1) or not(0) + * @property {string} csrf_k - CSRF key + * @property {boolean} logged_in_status - Is user logged in or not + */ diff --git a/users/login-user.js b/users/login-user.js index edaeb2b..6ef3294 100644 --- a/users/login-user.js +++ b/users/login-user.js @@ -93,6 +93,8 @@ module.exports = async function ({ key, payload, database, response, encryptionK * Make https request * * @description make a request to datasquirel.com + * + * @type {{ success: boolean, payload: DATASQUIREL_LoggedInUser | null }} */ const httpResponse = await new Promise((resolve, reject) => { const reqPayload = JSON.stringify({ diff --git a/utils/functions/sanitizeSql.js b/utils/functions/sanitizeSql.js new file mode 100644 index 0000000..ac88247 --- /dev/null +++ b/utils/functions/sanitizeSql.js @@ -0,0 +1,201 @@ +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +/** + * Sanitize SQL function + * ============================================================================== + * @description this function takes in a text(or number) and returns a sanitized + * text, usually without spaces + * + * @param {string|number|object} text - Text or number or object + * @param {boolean?} spaces - Allow spaces + * @param {RegExp?} regex - Regular expression, removes any match + * + * @returns {string|object} + */ +function sanitizeSql(text, spaces, regex) { + /** + * Initial Checks + * + * @description Initial Checks + */ + if (!text) return ""; + if (typeof text == "number" || typeof text == "boolean") return text; + if (typeof text == "string" && !text?.toString()?.match(/./)) return ""; + + if (typeof text == "object" && !Array.isArray(text)) { + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + const newObject = sanitizeObjects(text, spaces); + return newObject; + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + } else if (typeof text == "object" && Array.isArray(text)) { + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + const newArray = sanitizeArrays(text, spaces); + return newArray; + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + } + + // if (text?.toString()?.match(/\'|\"/)) { + // console.log("TEXT containing commas =>", text); + // return ""; + // } + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + /** + * Declare variables + * + * @description Declare "results" variable + */ + let finalText = text; + + if (regex) { + finalText = text.toString().replace(regex, ""); + } + + if (spaces) { + } else { + finalText = text + .toString() + .replace(/\n|\r|\n\r|\r\n/g, "") + .replace(/ /g, ""); + } + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + const escapeRegex = /select |insert |drop |delete |alter |create |exec | union | or | like | concat|LOAD_FILE|ASCII| COLLATE | HAVING | information_schema|DECLARE |\#|WAITFOR |delay |BENCHMARK |\/\*.*\*\//gi; + + finalText = finalText + .replace(/(?", text); + // return ""; + // } + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + return finalText; + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +/** + * Sanitize Objects Function + * ============================================================================== + * @description Sanitize objects in the form { key: "value" } + * + * @param {object} object - Database Full Name + * @param {boolean?} spaces - Allow spaces + * + * @returns {object} + */ +function sanitizeObjects(object, spaces) { + let objectUpdated = { ...object }; + const keys = Object.keys(objectUpdated); + + keys.forEach((key) => { + const value = objectUpdated[key]; + + if (!value) { + delete objectUpdated[key]; + return; + } + + if (typeof value == "string" || typeof value == "number") { + objectUpdated[key] = sanitizeSql(value, spaces); + } else if (typeof value == "object" && !Array.isArray(value)) { + objectUpdated[key] = sanitizeObjects(value, spaces); + } else if (typeof value == "object" && Array.isArray(value)) { + objectUpdated[key] = sanitizeArrays(value, spaces); + } + }); + + return objectUpdated; +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +/** + * Sanitize Objects Function + * ============================================================================== + * @description Sanitize objects in the form { key: "value" } + * + * @param {string[]|number[]|object[]} array - Database Full Name + * @param {boolean?} spaces - Allow spaces + * + * @returns {string[]|number[]|object[]} + */ +function sanitizeArrays(array, spaces) { + let arrayUpdated = [...array]; + + arrayUpdated.forEach((item, index) => { + const value = item; + + if (!value) { + arrayUpdated.splice(index, 1); + return; + } + + if (typeof item == "string" || typeof item == "number") { + arrayUpdated[index] = sanitizeSql(value, spaces); + } else if (typeof item == "object" && !Array.isArray(value)) { + arrayUpdated[index] = sanitizeObjects(value, spaces); + } else if (typeof item == "object" && Array.isArray(value)) { + arrayUpdated[index] = sanitizeArrays(item, spaces); + } + }); + + return arrayUpdated; +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +module.exports = sanitizeSql; diff --git a/utils/get.js b/utils/get.js index 9b41e59..671f554 100644 --- a/utils/get.js +++ b/utils/get.js @@ -15,7 +15,7 @@ const https = require("https"); /** * @typedef {Object} GetReturn * @property {boolean} success - Did the function run successfully? - * @property {(Object[]|string)} [payload=[]] - The Y Coordinate + * @property {(Object[]|string)} [payload=[]] - GET request results */ /** diff --git a/utils/post.js b/utils/post.js index e315c07..5ee0550 100644 --- a/utils/post.js +++ b/utils/post.js @@ -18,6 +18,17 @@ const https = require("https"); * @property {(Object[]|string)} [payload=[]] - The Y Coordinate */ +/** + * @typedef {object} PostDataPayload + * @property {string} action - "insert" | "update" | "delete" + * @property {string} table - Table name(slug) eg "blog_posts" + * @property {string} identifierColumnName - Table identifier field name => eg. "id" OR "email" + * @property {string} identifierValue - Corresponding value of the selected field name => This + * checks for duplicate, and the function will not run if this value is found + * @property {object} data - Table insert payload object => This must have keys that match + * table fields + */ + /** * ============================================================================== * Main Function @@ -27,13 +38,7 @@ const https = require("https"); * @param {Object} params - Single object passed * @param {string} params.key - API Key * @param {string} params.database - Database Name - * @param {({ - * action: [string="insert"], - * table: string, - * identifierColumnName: string, - * identifierValue: (string|number), - * data: object, - * } | string)} params.query - SQL query String or Request Object + * @param {PostDataPayload} params.query - SQL query String or Request Object * * @returns { Promise } - Return Object */