From 190598aa3f21df229d81e940bcb1435f4774e40e Mon Sep 17 00:00:00 2001 From: Benjamin Toby Date: Wed, 6 Nov 2024 07:26:23 +0100 Subject: [PATCH] Updates --- engine/query/get.js | 6 +- engine/query/post.js | 3 +- engine/query/utils/addDbEntry.js | 184 ----- engine/user/add-user.js | 3 +- engine/user/social/utils/handleSocialDb.js | 2 +- engine/user/update-user.js | 2 +- package-lock.json | 300 ++++++- package-shared/README.md | 4 + .../functions/backend/addMariadbUser.js | 66 ++ package-shared/functions/backend/api-cred.js | 43 + package-shared/functions/backend/db/add.js | 163 ++++ .../functions/backend/db/addDbEntry.js | 232 ++++++ .../functions/backend/db}/deleteDbEntry.js | 26 +- .../backend/db/pathTraversalCheck.js | 41 + .../functions/backend/db}/runQuery.js | 97 ++- .../functions/backend/db/sanitizeSql.js | 207 +++++ .../functions/backend/db}/updateDbEntry.js | 93 ++- package-shared/functions/backend/dbHandler.js | 90 +++ package-shared/functions/backend/decrypt.js | 29 + .../functions/backend/defaultFieldsRegexp.js | 15 + package-shared/functions/backend/encrypt.js | 43 + .../functions/backend/fullAccessDbHandler.js | 78 ++ .../backend/html/sanitizeHtmlOptions.js | 12 + .../functions/backend/noDatabaseDbHandler.js | 59 ++ .../functions/backend/parseDbResults.js | 76 ++ .../functions/backend/passwordHash.js | 31 + .../functions/backend/serverError.js | 55 ++ .../functions/backend/suSocketAuth.js | 38 + .../functions/backend/varDatabaseDbHandler.js | 116 +++ .../backend/varReadOnlyDatabaseDbHandler.js | 74 ++ package-shared/types/index.d.ts | 726 +++++++++++++++++ package-shared/types/index.js | 738 ++++++++++++++++++ .../utils/backend/global-db/DB_HANDLER.js | 48 ++ .../backend/global-db/DSQL_USER_DB_HANDLER.js | 129 +++ .../utils/backend/global-db/NO_DB_HANDLER.js | 63 ++ .../backend/global-db/ROOT_DB_HANDLER.js | 54 ++ .../utils/backend/global-db/index.js | 49 ++ package-shared/utils/ejson.js | 38 + package.json | 12 +- tsconfig.json | 2 +- users/update-user.js | 2 +- utils/get.js | 15 +- 42 files changed, 3766 insertions(+), 298 deletions(-) delete mode 100644 engine/query/utils/addDbEntry.js create mode 100644 package-shared/functions/backend/addMariadbUser.js create mode 100644 package-shared/functions/backend/api-cred.js create mode 100644 package-shared/functions/backend/db/add.js create mode 100644 package-shared/functions/backend/db/addDbEntry.js rename {engine/query/utils => package-shared/functions/backend/db}/deleteDbEntry.js (68%) create mode 100644 package-shared/functions/backend/db/pathTraversalCheck.js rename {engine/query/utils => package-shared/functions/backend/db}/runQuery.js (63%) create mode 100644 package-shared/functions/backend/db/sanitizeSql.js rename {engine/query/utils => package-shared/functions/backend/db}/updateDbEntry.js (69%) create mode 100644 package-shared/functions/backend/dbHandler.js create mode 100644 package-shared/functions/backend/decrypt.js create mode 100644 package-shared/functions/backend/defaultFieldsRegexp.js create mode 100644 package-shared/functions/backend/encrypt.js create mode 100644 package-shared/functions/backend/fullAccessDbHandler.js create mode 100644 package-shared/functions/backend/html/sanitizeHtmlOptions.js create mode 100644 package-shared/functions/backend/noDatabaseDbHandler.js create mode 100644 package-shared/functions/backend/parseDbResults.js create mode 100644 package-shared/functions/backend/passwordHash.js create mode 100644 package-shared/functions/backend/serverError.js create mode 100644 package-shared/functions/backend/suSocketAuth.js create mode 100644 package-shared/functions/backend/varDatabaseDbHandler.js create mode 100644 package-shared/functions/backend/varReadOnlyDatabaseDbHandler.js create mode 100644 package-shared/utils/backend/global-db/DB_HANDLER.js create mode 100644 package-shared/utils/backend/global-db/DSQL_USER_DB_HANDLER.js create mode 100644 package-shared/utils/backend/global-db/NO_DB_HANDLER.js create mode 100644 package-shared/utils/backend/global-db/ROOT_DB_HANDLER.js create mode 100644 package-shared/utils/backend/global-db/index.js create mode 100644 package-shared/utils/ejson.js diff --git a/engine/query/get.js b/engine/query/get.js index 34f5987..80c8370 100644 --- a/engine/query/get.js +++ b/engine/query/get.js @@ -3,9 +3,9 @@ * No imports found for this Module ==== MODULE TRACE END ==== */ -// @ts-check +const runQuery = require("../../package-shared/functions/backend/db/runQuery"); -const runQuery = require("./utils/runQuery"); +// @ts-check /** * @typedef {Object} LocalGetReturn @@ -39,7 +39,6 @@ async function localGet({ options, dbSchema }) { /** @type {string | undefined | any } */ const tableName = options?.tableName ? options.tableName : undefined; - const dbFullName = process.env.DSQL_DB_NAME || ""; /** @@ -71,6 +70,7 @@ async function localGet({ options, dbSchema }) { queryValuesArray: queryValues, dbSchema, tableName, + local: true, }); if (error) throw error; diff --git a/engine/query/post.js b/engine/query/post.js index 9136c57..ab0f4a0 100644 --- a/engine/query/post.js +++ b/engine/query/post.js @@ -1,6 +1,6 @@ // @ts-check -const runQuery = require("./utils/runQuery"); +const runQuery = require("../../package-shared/functions/backend/db/runQuery"); /** * Make a get request to Datasquirel API @@ -52,6 +52,7 @@ async function localPost({ options, dbSchema }) { dbSchema: dbSchema, queryValuesArray: queryValues, tableName, + local: true, }); if (error) throw error; diff --git a/engine/query/utils/addDbEntry.js b/engine/query/utils/addDbEntry.js deleted file mode 100644 index cf654b5..0000000 --- a/engine/query/utils/addDbEntry.js +++ /dev/null @@ -1,184 +0,0 @@ -// @ts-check - -/** - * Imports: Handle imports - */ -const encrypt = require("../../../functions/encrypt"); -const dbHandler = require("../../engine/utils/dbHandler"); -const updateDb = require("./updateDbEntry"); -const updateDbEntry = require("./updateDbEntry"); - -/** - * Add a db Entry Function - * ============================================================================== - * @description Description - * @async - * - * @param {object} params - An object containing the function parameters. - * "Read only" or "Full Access"? Defaults to "Read Only" - * @param {string} params.dbFullName - Database full name - * @param {string} params.tableName - Table name - * @param {*} params.data - Data to add - * @param {import("../../../package-shared/types").DSQL_TableSchemaType} [params.tableSchema] - Table schema - * @param {string} [params.duplicateColumnName] - Duplicate column name - * @param {string} [params.duplicateColumnValue] - Duplicate column value - * @param {boolean} [params.update] - Update this row if it exists - * @param {string} params.encryptionKey - Update this row if it exists - * @param {string} params.encryptionSalt - Update this row if it exists - * - * @returns {Promise<*>} - */ -async function addDbEntry({ - dbFullName, - tableName, - data, - tableSchema, - duplicateColumnName, - duplicateColumnValue, - update, - encryptionKey, - encryptionSalt, -}) { - /** - * Initialize variables - */ - - //////////////////////////////////////// - //////////////////////////////////////// - //////////////////////////////////////// - - /** - * Handle function logic - */ - - if (duplicateColumnName && typeof duplicateColumnName === "string") { - const duplicateValue = await dbHandler({ - database: dbFullName, - query: `SELECT * FROM \`${tableName}\` WHERE \`${duplicateColumnName}\`=?`, - values: [duplicateColumnValue || ""], - }); - - if (duplicateValue && duplicateValue[0] && !update) { - return null; - } else if (duplicateValue && duplicateValue[0] && update) { - return await updateDbEntry({ - dbFullName, - tableName, - data, - tableSchema, - identifierColumnName: duplicateColumnName, - identifierValue: duplicateColumnValue || "", - encryptionKey, - encryptionSalt, - }); - } - } - - /** - * Declare variables - * - * @description Declare "results" variable - */ - const dataKeys = Object.keys(data); - - let insertKeysArray = []; - let insertValuesArray = []; - - for (let i = 0; i < dataKeys.length; i++) { - try { - const dataKey = dataKeys[i]; - let value = data[dataKey]; - - const targetFieldSchemaArray = tableSchema - ? tableSchema?.fields?.filter( - (field) => field.fieldName == dataKey - ) - : null; - const targetFieldSchema = - targetFieldSchemaArray && targetFieldSchemaArray[0] - ? targetFieldSchemaArray[0] - : null; - - if (!value) continue; - - if (targetFieldSchema?.encrypted) { - value = encrypt({ data: value, encryptionKey, encryptionSalt }); - console.log("DSQL: Encrypted value =>", value); - } - - if (targetFieldSchema?.pattern) { - const pattern = new RegExp( - targetFieldSchema.pattern, - targetFieldSchema.patternFlags || "" - ); - if (!value?.toString()?.match(pattern)) { - console.log("DSQL: Pattern not matched =>", value); - value = ""; - } - } - - if (typeof value === "string" && !value.match(/./i)) { - value = { - toSqlString: function () { - return "NULL"; - }, - }; - } - - insertKeysArray.push("`" + dataKey + "`"); - - if (typeof value === "object") { - value = JSON.stringify(value); - } - - insertValuesArray.push(value); - } catch (/** @type {*} */ error) { - console.log("DSQL: Error in parsing data keys =>", error.message); - continue; - } - } - - //////////////////////////////////////// - - insertKeysArray.push("`date_created`"); - insertValuesArray.push(Date()); - - insertKeysArray.push("`date_created_code`"); - insertValuesArray.push(Date.now()); - - //////////////////////////////////////// - - insertKeysArray.push("`date_updated`"); - insertValuesArray.push(Date()); - - insertKeysArray.push("`date_updated_code`"); - insertValuesArray.push(Date.now()); - - //////////////////////////////////////// - - const query = `INSERT INTO \`${tableName}\` (${insertKeysArray.join( - "," - )}) VALUES (${insertValuesArray.map(() => "?").join(",")})`; - const queryValuesArray = insertValuesArray; - - const newInsert = await dbHandler({ - database: dbFullName, - query: query, - values: queryValuesArray, - }); - - //////////////////////////////////////// - //////////////////////////////////////// - //////////////////////////////////////// - - /** - * Return statement - */ - return newInsert; -} - -//////////////////////////////////////// -//////////////////////////////////////// -//////////////////////////////////////// - -module.exports = addDbEntry; diff --git a/engine/user/add-user.js b/engine/user/add-user.js index 96148fc..766f0b3 100644 --- a/engine/user/add-user.js +++ b/engine/user/add-user.js @@ -1,10 +1,9 @@ // @ts-check const hashPassword = require("../../functions/hashPassword"); +const addDbEntry = require("../../package-shared/functions/backend/db/addDbEntry"); const addUsersTableToDb = require("../engine/addUsersTableToDb"); const varDatabaseDbHandler = require("../engine/utils/varDatabaseDbHandler"); -const addDbEntry = require("../query/utils/addDbEntry"); -const runQuery = require("../query/utils/runQuery"); /** * Make a get request to Datasquirel API diff --git a/engine/user/social/utils/handleSocialDb.js b/engine/user/social/utils/handleSocialDb.js index f471a6c..baf9de3 100644 --- a/engine/user/social/utils/handleSocialDb.js +++ b/engine/user/social/utils/handleSocialDb.js @@ -8,8 +8,8 @@ const fs = require("fs"); const http = require("http"); const varDatabaseDbHandler = require("../../../engine/utils/varDatabaseDbHandler"); -const addDbEntry = require("../../../query/utils/addDbEntry"); const encrypt = require("../../../../functions/encrypt"); +const addDbEntry = require("../../../../package-shared/functions/backend/db/addDbEntry"); ////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////// diff --git a/engine/user/update-user.js b/engine/user/update-user.js index 79fed3e..2af40e0 100644 --- a/engine/user/update-user.js +++ b/engine/user/update-user.js @@ -1,6 +1,6 @@ // @ts-check -const updateDbEntry = require("../query/utils/updateDbEntry"); +const updateDbEntry = require("../../package-shared/functions/backend/db/updateDbEntry"); /** * @typedef {Object} LocalPostReturn diff --git a/package-lock.json b/package-lock.json index e135661..bb6dfa1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,32 +1,62 @@ { "name": "datasquirel", - "version": "2.1.1", + "version": "2.3.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "datasquirel", - "version": "2.1.1", + "version": "2.3.5", "license": "ISC", "dependencies": { + "@types/ace": "^0.0.52", + "@types/react": "^18.3.12", + "@types/tinymce": "^4.6.9", "dotenv": "^16.3.1", + "generate-password": "^1.7.1", + "lodash": "^4.17.21", "mysql": "^2.18.1", - "nodemailer": "^6.9.14" + "nodemailer": "^6.9.14", + "sanitize-html": "^2.13.1", + "serverless-mysql": "^1.5.5" }, "bin": { "dsql-dump": "engine/dump.js", "dsql-watch": "engine/dsql.js" }, "devDependencies": { + "@types/lodash": "^4.17.13", "@types/mysql": "^2.15.21", "@types/node": "^22.7.5" } }, + "node_modules/@types/ace": { + "version": "0.0.52", + "resolved": "https://registry.npmjs.org/@types/ace/-/ace-0.0.52.tgz", + "integrity": "sha512-YPF9S7fzpuyrxru+sG/rrTpZkC6gpHBPF14W3x70kqVOD+ks6jkYLapk4yceh36xej7K4HYxcyz9ZDQ2lTvwgQ==", + "license": "MIT" + }, + "node_modules/@types/jquery": { + "version": "3.5.32", + "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.32.tgz", + "integrity": "sha512-b9Xbf4CkMqS02YH8zACqN1xzdxc3cO735Qe5AbSUFmyOiaWAbcpqh9Wna+Uk0vgACvoQHpWDg2rGdHkYPLmCiQ==", + "license": "MIT", + "dependencies": { + "@types/sizzle": "*" + } + }, + "node_modules/@types/lodash": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.13.tgz", + "integrity": "sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/mysql": { "version": "2.15.21", "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.21.tgz", "integrity": "sha512-NPotx5CVful7yB+qZbWtXL2fA4e7aEHkihHLjklc6ID8aq7bhguHgeIoC1EmSNTAuCgI6ZXrjt2ZSaXnYX0EUg==", - "dev": true, + "devOptional": true, "dependencies": { "@types/node": "*" } @@ -35,12 +65,43 @@ "version": "22.7.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "undici-types": "~6.19.2" } }, + "node_modules/@types/prop-types": { + "version": "15.7.13", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.12", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz", + "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/sizzle": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.9.tgz", + "integrity": "sha512-xzLEyKB50yqCUPUJkIsrVvoWNfFUbIZI+RspLWt8u+tIW/BetMBZtgV2LY/2o+tYH8dRvQ+eoPf3NdhQCcLE2w==", + "license": "MIT" + }, + "node_modules/@types/tinymce": { + "version": "4.6.9", + "resolved": "https://registry.npmjs.org/@types/tinymce/-/tinymce-4.6.9.tgz", + "integrity": "sha512-pDxBUlV4v1jgJ97SlnVOSyf3KUy3OQ3s5Ddpfh1L9M5lXlBmX7TJ2OLSozx1WBxp91acHvYPWDwz2U/kMM1oxQ==", + "license": "MIT", + "dependencies": { + "@types/jquery": "*" + } + }, "node_modules/bignumber.js": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", @@ -54,6 +115,76 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/dotenv": { "version": "16.3.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", @@ -65,16 +196,80 @@ "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/generate-password": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/generate-password/-/generate-password-1.7.1.tgz", + "integrity": "sha512-9bVYY+16m7W7GczRBDqXE+VVuCX+bWNrfYKC/2p2JkZukFb2sKxT6E3zZ3mJGz7GMe5iRK0A/WawSL3jQfJuNQ==", + "license": "MIT" + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, "node_modules/mysql": { "version": "2.18.1", "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz", @@ -89,6 +284,24 @@ "node": ">= 0.6" } }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/nodemailer": { "version": "6.9.14", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.14.tgz", @@ -98,6 +311,46 @@ "node": ">=6.0.0" } }, + "node_modules/parse-srcset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", + "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -122,6 +375,41 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "node_modules/sanitize-html": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.13.1.tgz", + "integrity": "sha512-ZXtKq89oue4RP7abL9wp/9URJcqQNABB5GGJ2acW1sdO8JTVl92f4ygD7Yc9Ze09VAZhnt2zegeU0tbNsdcLYg==", + "license": "MIT", + "dependencies": { + "deepmerge": "^4.2.2", + "escape-string-regexp": "^4.0.0", + "htmlparser2": "^8.0.0", + "is-plain-object": "^5.0.0", + "parse-srcset": "^1.0.2", + "postcss": "^8.3.11" + } + }, + "node_modules/serverless-mysql": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/serverless-mysql/-/serverless-mysql-1.5.5.tgz", + "integrity": "sha512-QwaCtswn3GKCnqyVA0whwDFMIw91iKTeTvf6F++HoGiunfyvfJ2MdU8d3MKMQdKGNOXIvmUlLq/JVjxuPQxkrw==", + "license": "MIT", + "dependencies": { + "mysql": "^2.18.1" + }, + "optionalDependencies": { + "@types/mysql": "^2.15.6" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/sqlstring": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", @@ -142,7 +430,7 @@ "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/util-deprecate": { diff --git a/package-shared/README.md b/package-shared/README.md index b196a25..e8a664a 100644 --- a/package-shared/README.md +++ b/package-shared/README.md @@ -1,3 +1,7 @@ # Shared resources This directory contains data (mostly type definitions) shared by both the datasquirel NPM package and the datasquirel web app + +## Functions + +## Types diff --git a/package-shared/functions/backend/addMariadbUser.js b/package-shared/functions/backend/addMariadbUser.js new file mode 100644 index 0000000..8343def --- /dev/null +++ b/package-shared/functions/backend/addMariadbUser.js @@ -0,0 +1,66 @@ +// @ts-check + +const generator = require("generate-password"); +const DB_HANDLER = require("../../utils/backend/global-db/DB_HANDLER"); +const NO_DB_HANDLER = require("../../utils/backend/global-db/NO_DB_HANDLER"); +const encrypt = require("./encrypt"); +const addDbEntry = require("./db/addDbEntry"); + +/** + * # Add Mariadb User + * + * @description this function adds a Mariadb user to the database server + * + * @param {object} params - parameters object * + * @param {number | string} params.userId - invited user object + * + * @returns {Promise} new user auth object payload + */ +module.exports = async function addMariadbUser({ userId }) { + try { + const defaultMariadbUserHost = process.env.DSQL_DB_HOST || "127.0.0.1"; + + const username = `dsql_user_${userId}`; + const password = generator.generate({ + length: 16, + numbers: true, + symbols: true, + uppercase: true, + exclude: "*#.'`\"", + }); + const encryptedPassword = encrypt(password); + + await NO_DB_HANDLER( + `CREATE USER IF NOT EXISTS '${username}'@'127.0.0.1' IDENTIFIED BY '${password}' REQUIRE SSL` + ); + + const updateUser = await DB_HANDLER( + `UPDATE users SET mariadb_user = ?, mariadb_host = '127.0.0.1', mariadb_pass = ? WHERE id = ?`, + [username, encryptedPassword, userId] + ); + + const addMariadbUser = await addDbEntry({ + tableName: "mariadb_users", + data: { + user_id: userId, + username, + host: defaultMariadbUserHost, + password: encryptedPassword, + primary: "1", + grants: '[{"database":"*","table":"*","privileges":["ALL"]}]', + }, + dbContext: "Master", + }); + + console.log(`User ${userId} SQL credentials successfully added.`); + } catch (/** @type {any} */ error) { + console.log( + `Error in adding SQL user in 'addMariadbUser' function =>`, + error.message + ); + } +}; + +//////////////////////////////////////////////// +//////////////////////////////////////////////// +//////////////////////////////////////////////// diff --git a/package-shared/functions/backend/api-cred.js b/package-shared/functions/backend/api-cred.js new file mode 100644 index 0000000..0187272 --- /dev/null +++ b/package-shared/functions/backend/api-cred.js @@ -0,0 +1,43 @@ +// @ts-check + +const fs = require("fs"); +const decrypt = require("./decrypt"); + +/** @type {import("../../types").CheckApiCredentialsFn} */ +const grabApiCred = ({ key, database, table }) => { + try { + const allowedKeysPath = process.env.DSQL_API_KEYS_PATH; + + if (!allowedKeysPath) + throw new Error( + "process.env.DSQL_API_KEYS_PATH variable not found" + ); + + const ApiJSON = decrypt(key); + /** @type {import("../../types").ApiKeyObject} */ + const ApiObject = JSON.parse(ApiJSON || ""); + const isApiKeyValid = fs.existsSync( + `${allowedKeysPath}/${ApiObject.sign}` + ); + + if (!isApiKeyValid) return null; + if (!ApiObject.target_database) return ApiObject; + if (!database && ApiObject.target_database) return null; + const isDatabaseAllowed = ApiObject.target_database + ?.split(",") + .includes(String(database)); + + if (isDatabaseAllowed && !ApiObject.target_table) return ApiObject; + if (isDatabaseAllowed && !table && ApiObject.target_table) return null; + const isTableAllowed = ApiObject.target_table + ?.split(",") + .includes(String(table)); + if (isTableAllowed) return ApiObject; + return null; + } catch (/** @type {any} */ error) { + console.log(`api-cred ERROR: ${error.message}`); + return null; + } +}; + +module.exports = grabApiCred; diff --git a/package-shared/functions/backend/db/add.js b/package-shared/functions/backend/db/add.js new file mode 100644 index 0000000..e091924 --- /dev/null +++ b/package-shared/functions/backend/db/add.js @@ -0,0 +1,163 @@ +// @ts-check + +const fs = require("fs"); +const DB_HANDLER = require("../../../utils/backend/global-db/DB_HANDLER"); + +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ + +/** + * Add Database Entry + * ============================================================================== + * @param {object} params - foundUser if any + * @param {string} params.tableName - Table Name + * @param {any} params.data - Data to be added + * @param {string} [params.duplicateColumnName] - Duplicate Column Name + * @param {string | number} [params.duplicateColumnValue] - Duplicate Column Value + */ +module.exports = async function addDbEntry({ + tableName, + data, + duplicateColumnName, + duplicateColumnValue, +}) { + /** + * Check Duplicate if specified + * + * @description Check Duplicate if specified + */ + if (duplicateColumnName) { + let duplicateEntry = await DB_HANDLER( + `SELECT ${duplicateColumnName} FROM ${tableName} WHERE ${duplicateColumnName}='${duplicateColumnValue}'` + ); + + if (duplicateEntry && duplicateEntry[0]) return null; + } + + /** + * Declare variables + * + * @description Declare "results" variable + */ + const dataKeys = Object.keys(data); + + let insertKeysArray = []; + let insertValuesArray = []; + + for (let i = 0; i < dataKeys.length; i++) { + const dataKey = dataKeys[i]; + let dataValue = data[dataKey]; + // const correspondingColumnObject = dbColumns.filter((col) => col.Field === dataKey); + // const { Field, Type, Null, Key, Default, Extra } = correspondingColumnObject; + + if (!dataValue) continue; + + insertKeysArray.push("`" + dataKey + "`"); + + if (typeof dataValue === "object") { + dataValue = JSON.stringify(data[dataKey]); + } + + // let parsedDataValue = dataValue.toString().replace(/\'/g, "\\'"); + + insertValuesArray.push(dataValue); + } + + //////////////////////////////////////// + // @ts-ignore + let existingDateCreatedColumn = await DB_HANDLER( + `SHOW COLUMNS FROM \`${tableName}\` WHERE Field = 'date_created'` + ); + if (!existingDateCreatedColumn || !existingDateCreatedColumn[0]) { + // @ts-ignore + await DB_HANDLER( + `ALTER TABLE ${tableName} ADD COLUMN date_created VARCHAR(255) NOT NULL` + ); + } + + insertKeysArray.push("date_created"); + insertValuesArray.push(Date()); + + //////////////////////////////////////// + + // @ts-ignore + let existingDateCreatedCodeColumn = await DB_HANDLER( + `SHOW COLUMNS FROM ${tableName} WHERE Field = 'date_created_code'` + ); + if (!existingDateCreatedCodeColumn || !existingDateCreatedCodeColumn[0]) { + // @ts-ignore + await DB_HANDLER( + `ALTER TABLE ${tableName} ADD COLUMN date_created_code BIGINT NOT NULL` + ); + } + + insertKeysArray.push("date_created_code"); + insertValuesArray.push(Date.now()); + + //////////////////////////////////////// + + // @ts-ignore + let existingDateCodeColumn = await DB_HANDLER( + `SHOW COLUMNS FROM ${tableName} WHERE Field = 'date_code'` + ); + if (existingDateCodeColumn && existingDateCodeColumn[0]) { + insertKeysArray.push("date_code"); + insertValuesArray.push(Date.now()); + } + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + // @ts-ignore + let existingDateUpdatedColumn = await DB_HANDLER( + `SHOW COLUMNS FROM ${tableName} WHERE Field = 'date_updated'` + ); + if (!existingDateUpdatedColumn || !existingDateUpdatedColumn[0]) { + // @ts-ignore + await DB_HANDLER( + `ALTER TABLE ${tableName} ADD COLUMN date_updated VARCHAR(255) NOT NULL` + ); + } + + insertKeysArray.push("date_updated"); + insertValuesArray.push(Date()); + + //////////////////////////////////////// + + // @ts-ignore + let existingDateUpdatedCodeColumn = await DB_HANDLER( + `SHOW COLUMNS FROM ${tableName} WHERE Field = 'date_updated_code'` + ); + if (!existingDateUpdatedCodeColumn || !existingDateUpdatedCodeColumn[0]) { + // @ts-ignore + await DB_HANDLER( + `ALTER TABLE ${tableName} ADD COLUMN date_updated_code BIGINT NOT NULL` + ); + } + + insertKeysArray.push("date_updated_code"); + insertValuesArray.push(Date.now()); + + //////////////////////////////////////// + + const query = `INSERT INTO ${tableName} (${insertKeysArray.join( + "," + )}) VALUES (${insertValuesArray.map((val) => "?").join(",")})`; + const queryValuesArray = insertValuesArray; + + // @ts-ignore + const newInsert = await DB_HANDLER(query, queryValuesArray); + + //////////////////////////////////////// + + return newInsert; + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// +}; diff --git a/package-shared/functions/backend/db/addDbEntry.js b/package-shared/functions/backend/db/addDbEntry.js new file mode 100644 index 0000000..8beac5d --- /dev/null +++ b/package-shared/functions/backend/db/addDbEntry.js @@ -0,0 +1,232 @@ +// @ts-check + +/** + * Imports: Handle imports + */ +const encrypt = require("../encrypt"); +const sanitizeHtml = require("sanitize-html"); +const sanitizeHtmlOptions = require("../html/sanitizeHtmlOptions"); +const updateDb = require("./updateDbEntry"); +const updateDbEntry = require("./updateDbEntry"); +const _ = require("lodash"); +const DB_HANDLER = require("../../../utils/backend/global-db/DB_HANDLER"); +const DSQL_USER_DB_HANDLER = require("../../../utils/backend/global-db/DSQL_USER_DB_HANDLER"); + +/** + * Add a db Entry Function + * ============================================================================== + * @description Description + * @async + * + * @param {object} params - An object containing the function parameters. + * @param {("Master" | "Dsql User")} [params.dbContext] - What is the database context? "Master" + * or "Dsql User". Defaults to "Master" + * @param {("Read Only" | "Full Access")} [params.paradigm] - What is the paradigm for "Dsql User"? + * "Read only" or "Full Access"? Defaults to "Read Only" + * @param {string} [params.dbFullName] - Database full name + * @param {string} params.tableName - Table name + * @param {any} params.data - Data to add + * @param {import("../../../types").DSQL_TableSchemaType} [params.tableSchema] - Table schema + * @param {string} [params.duplicateColumnName] - Duplicate column name + * @param {string} [params.duplicateColumnValue] - Duplicate column value + * @param {boolean} [params.update] - Update this row if it exists + * @param {string} [params.encryptionKey] - Update this row if it exists + * @param {string} [params.encryptionSalt] - Update this row if it exists + * + * @returns {Promise} + */ +async function addDbEntry({ + dbContext, + paradigm, + dbFullName, + tableName, + data, + tableSchema, + duplicateColumnName, + duplicateColumnValue, + update, + encryptionKey, + encryptionSalt, +}) { + /** + * Initialize variables + */ + const isMaster = dbContext?.match(/dsql.user/i) + ? false + : dbFullName && !dbFullName.match(/^datasquirel$/) + ? false + : true; + + /** @type { any } */ + const dbHandler = isMaster ? DB_HANDLER : DSQL_USER_DB_HANDLER; + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + if (data?.["date_created_timestamp"]) delete data["date_created_timestamp"]; + if (data?.["date_updated_timestamp"]) delete data["date_updated_timestamp"]; + if (data?.["date_updated"]) delete data["date_updated"]; + if (data?.["date_updated_code"]) delete data["date_updated_code"]; + if (data?.["date_created"]) delete data["date_created"]; + if (data?.["date_created_code"]) delete data["date_created_code"]; + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + /** + * Handle function logic + */ + + if (duplicateColumnName && typeof duplicateColumnName === "string") { + const duplicateValue = isMaster + ? await dbHandler( + `SELECT * FROM \`${tableName}\` WHERE \`${duplicateColumnName}\`=?`, + [duplicateColumnValue] + ) + : await dbHandler({ + paradigm: "Read Only", + database: dbFullName, + queryString: `SELECT * FROM \`${tableName}\` WHERE \`${duplicateColumnName}\`=?`, + queryValues: [duplicateColumnValue], + }); + + if (duplicateValue?.[0] && !update) { + return null; + } else if (duplicateValue && duplicateValue[0] && update) { + return await updateDbEntry({ + dbContext, + paradigm, + dbFullName, + tableName, + data, + tableSchema, + encryptionKey, + encryptionSalt, + identifierColumnName: duplicateColumnName, + identifierValue: duplicateColumnValue || "", + }); + } + } + + /** + * Declare variables + * + * @description Declare "results" variable + */ + const dataKeys = Object.keys(data); + + let insertKeysArray = []; + let insertValuesArray = []; + + for (let i = 0; i < dataKeys.length; i++) { + try { + const dataKey = dataKeys[i]; + // @ts-ignore + let value = data?.[dataKey]; + + const targetFieldSchemaArray = tableSchema + ? tableSchema?.fields?.filter( + (field) => field.fieldName == dataKey + ) + : null; + const targetFieldSchema = + targetFieldSchemaArray && targetFieldSchemaArray[0] + ? targetFieldSchemaArray[0] + : null; + + if (value == null || value == undefined) continue; + + if (targetFieldSchema?.encrypted) { + value = encrypt(value, encryptionKey, encryptionSalt); + console.log("DSQL: Encrypted value =>", value); + } + + if (targetFieldSchema?.richText) { + value = sanitizeHtml(value, sanitizeHtmlOptions); + } + + if (targetFieldSchema?.pattern) { + const pattern = new RegExp( + targetFieldSchema.pattern, + targetFieldSchema.patternFlags || "" + ); + if (!pattern.test(value)) { + console.log("DSQL: Pattern not matched =>", value); + value = ""; + } + } + + insertKeysArray.push("`" + dataKey + "`"); + + if (typeof value === "object") { + value = JSON.stringify(value); + } + + if (typeof value == "number") { + insertValuesArray.push(String(value)); + } else { + insertValuesArray.push(value); + } + } catch (/** @type {any} */ error) { + console.log("DSQL: Error in parsing data keys =>", error.message); + continue; + } + } + + //////////////////////////////////////// + + if (!data?.["date_created"]) { + insertKeysArray.push("`date_created`"); + insertValuesArray.push(Date()); + } + + if (!data?.["date_created_code"]) { + insertKeysArray.push("`date_created_code`"); + insertValuesArray.push(Date.now()); + } + + //////////////////////////////////////// + + if (!data?.["date_updated"]) { + insertKeysArray.push("`date_updated`"); + insertValuesArray.push(Date()); + } + + if (!data?.["date_updated_code"]) { + insertKeysArray.push("`date_updated_code`"); + insertValuesArray.push(Date.now()); + } + + //////////////////////////////////////// + + const query = `INSERT INTO \`${tableName}\` (${insertKeysArray.join( + "," + )}) VALUES (${insertValuesArray.map(() => "?").join(",")})`; + const queryValuesArray = insertValuesArray; + + const newInsert = isMaster + ? await dbHandler(query, queryValuesArray) + : await dbHandler({ + paradigm, + database: dbFullName, + queryString: query, + queryValues: queryValuesArray, + }); + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + /** + * Return statement + */ + return newInsert; +} + +//////////////////////////////////////// +//////////////////////////////////////// +//////////////////////////////////////// + +module.exports = addDbEntry; diff --git a/engine/query/utils/deleteDbEntry.js b/package-shared/functions/backend/db/deleteDbEntry.js similarity index 68% rename from engine/query/utils/deleteDbEntry.js rename to package-shared/functions/backend/db/deleteDbEntry.js index bdbd31e..355c8db 100644 --- a/engine/query/utils/deleteDbEntry.js +++ b/package-shared/functions/backend/db/deleteDbEntry.js @@ -1,6 +1,7 @@ // @ts-check -const dbHandler = require("../../engine/utils/dbHandler"); +const DB_HANDLER = require("../../../utils/backend/global-db/DB_HANDLER"); +const DSQL_USER_DB_HANDLER = require("../../../utils/backend/global-db/DSQL_USER_DB_HANDLER"); /** * Imports: Handle imports @@ -19,7 +20,7 @@ const dbHandler = require("../../engine/utils/dbHandler"); * "Read only" or "Full Access"? Defaults to "Read Only" * @param {string} params.dbFullName - Database full name * @param {string} params.tableName - Table name - * @param {import("../../../package-shared/types").DSQL_TableSchemaType} [params.tableSchema] - Table schema + * @param {import("../../../types").DSQL_TableSchemaType} [params.tableSchema] - Table schema * @param {string} params.identifierColumnName - Update row identifier column name * @param {string|number} params.identifierValue - Update row identifier column value * @@ -37,6 +38,14 @@ async function deleteDbEntry({ /** * Check if data is valid */ + const isMaster = dbContext?.match(/dsql.user/i) + ? false + : dbFullName && !dbFullName.match(/^datasquirel$/) + ? false + : true; + + /** @type { (a1:any, a2?:any) => any } */ + const dbHandler = isMaster ? DB_HANDLER : DSQL_USER_DB_HANDLER; //////////////////////////////////////// //////////////////////////////////////// @@ -49,11 +58,14 @@ async function deleteDbEntry({ */ const query = `DELETE FROM ${tableName} WHERE \`${identifierColumnName}\`=?`; - const deletedEntry = await dbHandler({ - query: query, - database: dbFullName, - values: [identifierValue], - }); + const deletedEntry = isMaster + ? await dbHandler(query, [identifierValue]) + : await dbHandler({ + paradigm, + queryString: query, + database: dbFullName, + queryValues: [identifierValue], + }); //////////////////////////////////////// //////////////////////////////////////// diff --git a/package-shared/functions/backend/db/pathTraversalCheck.js b/package-shared/functions/backend/db/pathTraversalCheck.js new file mode 100644 index 0000000..2ac73b9 --- /dev/null +++ b/package-shared/functions/backend/db/pathTraversalCheck.js @@ -0,0 +1,41 @@ +// @ts-check + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +/** + * Sanitize SQL function + * ============================================================================== + * @description this function takes in a text(or number) and returns a sanitized + * text, usually without spaces + * + * @param {string|number} text - Text or number or object + * + * @returns {string} + */ +function pathTraversalCheck(text) { + /** + * Initial Checks + * + * @description Initial Checks + */ + + return text.toString().replace(/\//g, ""); + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +module.exports = pathTraversalCheck; diff --git a/engine/query/utils/runQuery.js b/package-shared/functions/backend/db/runQuery.js similarity index 63% rename from engine/query/utils/runQuery.js rename to package-shared/functions/backend/db/runQuery.js index ceb49f0..007ff00 100644 --- a/engine/query/utils/runQuery.js +++ b/package-shared/functions/backend/db/runQuery.js @@ -1,21 +1,25 @@ /** # MODULE TRACE ====================================================================== - * Detected 4 files that call this module. The files are listed below: + * Detected 3 files that call this module. The files are listed below: ====================================================================== - * `require` Statement Found in [get.js](d:\GitHub\dsql\engine\query\get.js) - * `require` Statement Found in [post.js](d:\GitHub\dsql\engine\query\post.js) - * `require` Statement Found in [add-user.js](d:\GitHub\dsql\engine\user\add-user.js) - * `require` Statement Found in [update-user.js](d:\GitHub\dsql\engine\user\update-user.js) + * `import` Statement Found in [get.js] => file:///d:\GitHub\datasquirel\pages\api\query\get.js + * `import` Statement Found in [post.js] => file:///d:\GitHub\datasquirel\pages\api\query\post.js + * `import` Statement Found in [add-user.js] => file:///d:\GitHub\datasquirel\pages\api\user\add-user.js ==== MODULE TRACE END ==== */ // @ts-check const fs = require("fs"); +const fullAccessDbHandler = require("../fullAccessDbHandler"); +const varReadOnlyDatabaseDbHandler = require("../varReadOnlyDatabaseDbHandler"); +const serverError = require("../serverError"); + const addDbEntry = require("./addDbEntry"); const updateDbEntry = require("./updateDbEntry"); const deleteDbEntry = require("./deleteDbEntry"); -const varDatabaseDbHandler = require("../../engine/utils/varDatabaseDbHandler"); +const DB_HANDLER = require("../../../utils/backend/global-db/DB_HANDLER"); +const parseDbResults = require("../parseDbResults"); /** ****************************************************************************** */ /** ****************************************************************************** */ @@ -29,13 +33,14 @@ const varDatabaseDbHandler = require("../../engine/utils/varDatabaseDbHandler"); * ============================================================================== * @param {object} params - An object containing the function parameters. * @param {string} params.dbFullName - Database full name. Eg. "datasquire_user_2_test" - * @param {*} params.query - Query string or object + * @param {string | any} params.query - Query string or object * @param {boolean} [params.readOnly] - Is this operation read only? - * @param {import("../../../package-shared/types").DSQL_DatabaseSchemaType} [params.dbSchema] - Database schema + * @param {boolean} [params.local] - Is this operation read only? + * @param {import("../../../types").DSQL_DatabaseSchemaType} [params.dbSchema] - Database schema * @param {string[]} [params.queryValuesArray] - An optional array of query values if "?" is used in the query string * @param {string} [params.tableName] - Table Name * - * @return {Promise<{result: *, error?: *}>} + * @return {Promise} */ async function runQuery({ dbFullName, @@ -44,16 +49,20 @@ async function runQuery({ dbSchema, queryValuesArray, tableName, + local, }) { /** * Declare variables * * @description Declare "results" variable */ - const encryptionKey = process.env.DSQL_ENCRYPTION_KEY || ""; - const encryptionSalt = process.env.DSQL_ENCRYPTION_SALT || ""; - let result, error, tableSchema; + /** @type {any} */ + let result; + /** @type {any} */ + let error; + /** @type {import("../../../types").DSQL_TableSchemaType | undefined} */ + let tableSchema; if (dbSchema) { try { @@ -68,7 +77,9 @@ async function runQuery({ tableSchema = dbSchema.tables.filter( (tb) => tb?.tableName === table )[0]; - } catch (_err) {} + } catch (_err) { + // console.log("ERROR getting tableSchema: ", _err.message); + } } /** @@ -78,12 +89,29 @@ async function runQuery({ */ try { if (typeof query === "string") { - result = await varDatabaseDbHandler({ - queryString: query, - queryValuesArray, - database: dbFullName, - tableSchema, - }); + if (local) { + const rawResults = await DB_HANDLER(query, queryValuesArray); + result = tableSchema + ? parseDbResults({ + unparsedResults: rawResults, + tableSchema, + }) + : rawResults; + } else if (readOnly) { + result = await varReadOnlyDatabaseDbHandler({ + queryString: query, + queryValuesArray, + database: dbFullName, + tableSchema, + }); + } else { + result = await fullAccessDbHandler({ + queryString: query, + queryValuesArray, + database: dbFullName, + tableSchema, + }); + } } else if (typeof query === "object") { /** * Declare variables @@ -104,6 +132,8 @@ async function runQuery({ switch (action.toLowerCase()) { case "insert": result = await addDbEntry({ + dbContext: local ? "Master" : "Dsql User", + paradigm: "Full Access", dbFullName: dbFullName, tableName: table, data: data, @@ -111,8 +141,6 @@ async function runQuery({ duplicateColumnName, duplicateColumnValue, tableSchema, - encryptionKey, - encryptionSalt, }); if (!result?.insertId) { @@ -123,7 +151,7 @@ async function runQuery({ case "update": result = await updateDbEntry({ - dbContext: "Dsql User", + dbContext: local ? "Master" : "Dsql User", paradigm: "Full Access", dbFullName: dbFullName, tableName: table, @@ -131,15 +159,13 @@ async function runQuery({ identifierColumnName, identifierValue, tableSchema, - encryptionKey, - encryptionSalt, }); break; case "delete": result = await deleteDbEntry({ - dbContext: "Dsql User", + dbContext: local ? "Master" : "Dsql User", paradigm: "Full Access", dbFullName: dbFullName, tableName: table, @@ -151,12 +177,7 @@ async function runQuery({ break; default: - console.log("Unhandled Query"); - console.log("Query Recieved =>", query); - result = { - result: null, - error: "Unhandled Query", - }; + result = null; break; } } @@ -164,14 +185,12 @@ async function runQuery({ //////////////////////////////////////// //////////////////////////////////////// //////////////////////////////////////// - } catch (/** @type {*} */ error) { - console.log("Error in Running Query =>", error.message); - console.log("Query Recieved =>", query); - - result = { - result: null, - error: "Error in running Query => " + error.message, - }; + } catch (/** @type {any} */ error) { + serverError({ + component: "functions/backend/runQuery", + message: error.message, + }); + result = null; error = error.message; } diff --git a/package-shared/functions/backend/db/sanitizeSql.js b/package-shared/functions/backend/db/sanitizeSql.js new file mode 100644 index 0000000..360d7d6 --- /dev/null +++ b/package-shared/functions/backend/db/sanitizeSql.js @@ -0,0 +1,207 @@ +// @ts-check + +const _ = require("lodash"); + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +/** + * Sanitize SQL function + * ============================================================================== + * @description this function takes in a text(or number) and returns a sanitized + * text, usually without spaces + * + * @param {any} text - Text or number or object + * @param {boolean} [spaces] - Allow spaces + * @param {RegExp?} [regex] - Regular expression, removes any match + * + * @returns {any} + */ +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 {any} object - Database Full Name + * @param {boolean} [spaces] - Allow spaces + * + * @returns {object} + */ +function sanitizeObjects(object, spaces) { + /** @type {any} */ + 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 {any[]} array - Database Full Name + * @param {boolean} [spaces] - Allow spaces + * + * @returns {string[]|number[]|object[]} + */ +function sanitizeArrays(array, spaces) { + let arrayUpdated = _.cloneDeep(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/engine/query/utils/updateDbEntry.js b/package-shared/functions/backend/db/updateDbEntry.js similarity index 69% rename from engine/query/utils/updateDbEntry.js rename to package-shared/functions/backend/db/updateDbEntry.js index 9f2a327..93e103b 100644 --- a/engine/query/utils/updateDbEntry.js +++ b/package-shared/functions/backend/db/updateDbEntry.js @@ -1,11 +1,13 @@ // @ts-check -const encrypt = require("../../../functions/encrypt"); -const dbHandler = require("../../engine/utils/dbHandler"); - /** * Imports: Handle imports */ +const encrypt = require("../encrypt"); +const sanitizeHtml = require("sanitize-html"); +const sanitizeHtmlOptions = require("../html/sanitizeHtmlOptions"); +const DB_HANDLER = require("../../../utils/backend/global-db/DB_HANDLER"); +const DSQL_USER_DB_HANDLER = require("../../../utils/backend/global-db/DSQL_USER_DB_HANDLER"); /** * Update DB Function @@ -18,14 +20,14 @@ const dbHandler = require("../../engine/utils/dbHandler"); * or "Dsql User". Defaults to "Master" * @param {("Read Only" | "Full Access")} [params.paradigm] - What is the paradigm for "Dsql User"? * "Read only" or "Full Access"? Defaults to "Read Only" - * @param {string} params.dbFullName - Database full name + * @param {string} [params.dbFullName] - Database full name * @param {string} params.tableName - Table name - * @param {*} params.data - Data to add - * @param {import("../../../package-shared/types").DSQL_TableSchemaType} [params.tableSchema] - Table schema + * @param {string} [params.encryptionKey] + * @param {string} [params.encryptionSalt] + * @param {any} params.data - Data to add + * @param {import("../../../types").DSQL_TableSchemaType} [params.tableSchema] - Table schema * @param {string} params.identifierColumnName - Update row identifier column name * @param {string | number} params.identifierValue - Update row identifier column value - * @param {string} params.encryptionKey - Encryption key - * @param {string} params.encryptionSalt - Encryption salt * * @returns {Promise} */ @@ -46,6 +48,15 @@ async function updateDbEntry({ */ if (!data || !Object.keys(data).length) return null; + const isMaster = dbContext?.match(/dsql.user/i) + ? false + : dbFullName && !dbFullName.match(/^datasquirel$/) + ? false + : true; + + /** @type {(a1:any, a2?:any)=> any } */ + const dbHandler = isMaster ? DB_HANDLER : DSQL_USER_DB_HANDLER; + //////////////////////////////////////// //////////////////////////////////////// //////////////////////////////////////// @@ -60,14 +71,10 @@ async function updateDbEntry({ let updateKeyValueArray = []; let updateValues = []; - /** - * Declare variables - * - * @description Declare "results" variable - */ for (let i = 0; i < dataKeys.length; i++) { try { const dataKey = dataKeys[i]; + // @ts-ignore let value = data[dataKey]; const targetFieldSchemaArray = tableSchema @@ -80,22 +87,31 @@ async function updateDbEntry({ ? targetFieldSchemaArray[0] : null; - if (typeof value == "undefined") continue; - if ( - typeof value !== "string" && - typeof value !== "number" && - !value - ) - continue; + if (value == null || value == undefined) continue; + + if (targetFieldSchema?.richText) { + value = sanitizeHtml(value, sanitizeHtmlOptions); + } if (targetFieldSchema?.encrypted) { - value = encrypt({ data: value, encryptionKey, encryptionSalt }); + value = encrypt(value, encryptionKey, encryptionSalt); } if (typeof value === "object") { value = JSON.stringify(value); } + if (targetFieldSchema?.pattern) { + const pattern = new RegExp( + targetFieldSchema.pattern, + targetFieldSchema.patternFlags || "" + ); + if (!pattern.test(value)) { + console.log("DSQL: Pattern not matched =>", value); + value = ""; + } + } + if (typeof value === "string" && value.match(/^null$/i)) { value = { toSqlString: function () { @@ -104,17 +120,6 @@ async function updateDbEntry({ }; } - if (targetFieldSchema?.pattern) { - const pattern = new RegExp( - targetFieldSchema.pattern, - targetFieldSchema.patternFlags || "" - ); - if (!value?.toString()?.match(pattern)) { - console.log("DSQL: Pattern not matched =>", value); - value = ""; - } - } - if (typeof value === "string" && !value.match(/./i)) { value = { toSqlString: function () { @@ -123,14 +128,17 @@ async function updateDbEntry({ }; } - if (!value && typeof value == "number" && value != 0) continue; - updateKeyValueArray.push(`\`${dataKey}\`=?`); - updateValues.push(value); + + if (typeof value == "number") { + updateValues.push(String(value)); + } else { + updateValues.push(value); + } //////////////////////////////////////// //////////////////////////////////////// - } catch (/** @type {*} */ error) { + } catch (/** @type {any} */ error) { //////////////////////////////////////// //////////////////////////////////////// @@ -157,11 +165,14 @@ async function updateDbEntry({ updateValues.push(identifierValue); - const updatedEntry = await dbHandler({ - database: dbFullName, - query: query, - values: updateValues, - }); + const updatedEntry = isMaster + ? await dbHandler(query, updateValues) + : await dbHandler({ + paradigm, + database: dbFullName, + queryString: query, + queryValues: updateValues, + }); //////////////////////////////////////// //////////////////////////////////////// diff --git a/package-shared/functions/backend/dbHandler.js b/package-shared/functions/backend/dbHandler.js new file mode 100644 index 0000000..283ad90 --- /dev/null +++ b/package-shared/functions/backend/dbHandler.js @@ -0,0 +1,90 @@ +// @ts-check + +const fs = require("fs"); +const serverError = require("./serverError"); + +const mysql = require("serverless-mysql"); +const path = require("path"); + +const SSL_DIR = "/app/ssl"; + +const connection = mysql({ + config: { + host: process.env.DSQL_DB_HOST, + user: process.env.DSQL_DB_USERNAME, + password: process.env.DSQL_DB_PASSWORD, + database: process.env.DSQL_DB_NAME, + charset: "utf8mb4", + ssl: { + ca: fs.readFileSync(`${SSL_DIR}/ca-cert.pem`), + }, + }, +}); + +/** + * Main DB Handler Function + * ============================================================================== + * @async + * + * @param {any} args + * @returns {Promise} + */ +module.exports = async function dbHandler(...args) { + process.env.NODE_ENV?.match(/dev/) && + fs.appendFileSync( + "./.tmp/sqlQuery.sql", + args[0] + "\n" + Date() + "\n\n\n", + "utf8" + ); + + /** + * Declare variables + * + * @description Declare "results" variable + */ + let results; + + /** + * Fetch from db + * + * @description Fetch data from db if no cache + */ + try { + results = await new Promise((resolve, reject) => { + // @ts-ignore + connection.query(...args, (error, result, fields) => { + if (error) { + resolve({ error: error.message }); + } else { + resolve(result); + } + }); + }); + + await connection.end(); + } catch (/** @type {any} */ error) { + fs.appendFileSync( + "./.tmp/dbErrorLogs.txt", + JSON.stringify(error, null, 4) + "\n" + Date() + "\n\n\n", + "utf8" + ); + + results = null; + + serverError({ + component: "dbHandler", + message: error.message, + }); + } + + /** + * Return results + * + * @description Return results add to cache if "req" param is passed + */ + if (results) { + return JSON.parse(JSON.stringify(results)); + } else { + return null; + } +}; diff --git a/package-shared/functions/backend/decrypt.js b/package-shared/functions/backend/decrypt.js new file mode 100644 index 0000000..b4b12d1 --- /dev/null +++ b/package-shared/functions/backend/decrypt.js @@ -0,0 +1,29 @@ +// @ts-check + +const { scryptSync, createDecipheriv } = require("crypto"); +const { Buffer } = require("buffer"); + +/** + * @param {string} encryptedString + * @returns {string | null} + */ +const decrypt = (encryptedString) => { + const algorithm = "aes-192-cbc"; + const password = process.env.DSQL_ENCRYPTION_PASSWORD || ""; + const salt = process.env.DSQL_ENCRYPTION_SALT || ""; + + let key = scryptSync(password, salt, 24); + let iv = Buffer.alloc(16, 0); + // @ts-ignore + const decipher = createDecipheriv(algorithm, key, iv); + + try { + let decrypted = decipher.update(encryptedString, "hex", "utf8"); + decrypted += decipher.final("utf8"); + return decrypted; + } catch (error) { + return null; + } +}; + +module.exports = decrypt; diff --git a/package-shared/functions/backend/defaultFieldsRegexp.js b/package-shared/functions/backend/defaultFieldsRegexp.js new file mode 100644 index 0000000..080dea6 --- /dev/null +++ b/package-shared/functions/backend/defaultFieldsRegexp.js @@ -0,0 +1,15 @@ +// @ts-check + +/** + * Regular expression to match default fields + * + * @description Regular expression to match default fields + */ +const defaultFieldsRegexp = + /^id$|^uuid$|^date_created$|^date_created_code$|^date_created_timestamp$|^date_updated$|^date_updated_code$|^date_updated_timestamp$/; + +//////////////////////////////////////// +//////////////////////////////////////// +//////////////////////////////////////// + +module.exports = defaultFieldsRegexp; diff --git a/package-shared/functions/backend/encrypt.js b/package-shared/functions/backend/encrypt.js new file mode 100644 index 0000000..b80807d --- /dev/null +++ b/package-shared/functions/backend/encrypt.js @@ -0,0 +1,43 @@ +// @ts-check + +const { scryptSync, createCipheriv } = require("crypto"); +const { Buffer } = require("buffer"); +const serverError = require("./serverError"); + +/** + * @async + * @param {string} data + * @param {string} [encryptionKey] + * @param {string} [encryptionSalt] + * @returns {string | null} + */ +const encrypt = (data, encryptionKey, encryptionSalt) => { + const algorithm = "aes-192-cbc"; + const password = encryptionKey + ? encryptionKey + : process.env.DSQL_ENCRYPTION_PASSWORD || ""; + + /** ********************* Generate key */ + const salt = encryptionSalt + ? encryptionSalt + : process.env.DSQL_ENCRYPTION_SALT || ""; + let key = scryptSync(password, salt, 24); + let iv = Buffer.alloc(16, 0); + // @ts-ignore + const cipher = createCipheriv(algorithm, key, iv); + + /** ********************* Encrypt data */ + try { + let encrypted = cipher.update(data, "utf8", "hex"); + encrypted += cipher.final("hex"); + return encrypted; + } catch (/** @type {any} */ error) { + serverError({ + component: "encrypt", + message: error.message, + }); + return null; + } +}; + +module.exports = encrypt; diff --git a/package-shared/functions/backend/fullAccessDbHandler.js b/package-shared/functions/backend/fullAccessDbHandler.js new file mode 100644 index 0000000..d789f1d --- /dev/null +++ b/package-shared/functions/backend/fullAccessDbHandler.js @@ -0,0 +1,78 @@ +// @ts-check + +const DSQL_USER_DB_HANDLER = require("../../utils/backend/global-db/DSQL_USER_DB_HANDLER"); +const parseDbResults = require("./parseDbResults"); +const serverError = require("./serverError"); + +/** + * + * @param {object} param0 + * @param {string} param0.queryString + * @param {string} param0.database + * @param {boolean} [param0.local] + * @param {import("../../types").DSQL_TableSchemaType | null} [param0.tableSchema] + * @param {string[]} [param0.queryValuesArray] + * @returns + */ +module.exports = async function fullAccessDbHandler({ + queryString, + database, + tableSchema, + queryValuesArray, + local, +}) { + /** + * Declare variables + * + * @description Declare "results" variable + */ + let results; + + /** + * Fetch from db + * + * @description Fetch data from db if no cache + */ + try { + /** ********************* Run Query */ + + results = await DSQL_USER_DB_HANDLER({ + paradigm: "Full Access", + database, + queryString, + queryValues: queryValuesArray, + }); + + //////////////////////////////////////// + } catch (/** @type {any} */ error) { + //////////////////////////////////////// + + serverError({ + component: "fullAccessDbHandler", + message: error.message, + }); + + /** + * Return error + */ + return error.message; + } + + /** + * Return results + * + * @description Return results add to cache if "req" param is passed + */ + if (results && tableSchema) { + const unparsedResults = results; + const parsedResults = await parseDbResults({ + unparsedResults: unparsedResults, + tableSchema: tableSchema, + }); + return parsedResults; + } else if (results) { + return results; + } else { + return null; + } +}; diff --git a/package-shared/functions/backend/html/sanitizeHtmlOptions.js b/package-shared/functions/backend/html/sanitizeHtmlOptions.js new file mode 100644 index 0000000..fecd803 --- /dev/null +++ b/package-shared/functions/backend/html/sanitizeHtmlOptions.js @@ -0,0 +1,12 @@ +// @ts-check + +const sanitizeHtmlOptions = { + allowedTags: ["b", "i", "em", "strong", "a", "p", "span", "ul", "ol", "li", "h1", "h2", "h3", "h4", "h5", "h6", "img", "div", "button", "pre", "code", "br"], + allowedAttributes: { + a: ["href"], + img: ["src", "alt", "width", "height", "class", "style"], + "*": ["style", "class"], + }, +}; + +module.exports = sanitizeHtmlOptions; diff --git a/package-shared/functions/backend/noDatabaseDbHandler.js b/package-shared/functions/backend/noDatabaseDbHandler.js new file mode 100644 index 0000000..081a3c6 --- /dev/null +++ b/package-shared/functions/backend/noDatabaseDbHandler.js @@ -0,0 +1,59 @@ +// @ts-check + +const fs = require("fs"); +const serverError = require("./serverError"); +const NO_DB_HANDLER = require("../../../package-shared/utils/backend/global-db/NO_DB_HANDLER"); + +/** + * Create database from Schema Function + * ============================================================================== + * @param {string} queryString - Query String + * @returns {Promise} + */ +module.exports = async function noDatabaseDbHandler(queryString) { + process.env.NODE_ENV?.match(/dev/) && + fs.appendFileSync( + "./.tmp/sqlQuery.sql", + queryString + "\n" + Date() + "\n\n\n", + "utf8" + ); + + /** + * Declare variables + * + * @description Declare "results" variable + */ + let results; + + /** + * Fetch from db + * + * @description Fetch data from db if no cache + */ + try { + /** ********************* Run Query */ + results = await NO_DB_HANDLER(queryString); + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + } catch (/** @type {any} */ error) { + serverError({ + component: "noDatabaseDbHandler", + message: error.message, + }); + + console.log("ERROR in noDatabaseDbHandler =>", error.message); + } + + /** + * Return results + * + * @description Return results add to cache if "req" param is passed + */ + if (results) { + return results; + } else { + return null; + } +}; diff --git a/package-shared/functions/backend/parseDbResults.js b/package-shared/functions/backend/parseDbResults.js new file mode 100644 index 0000000..2d5cc7e --- /dev/null +++ b/package-shared/functions/backend/parseDbResults.js @@ -0,0 +1,76 @@ +// @ts-check + +const decrypt = require("./decrypt"); +const defaultFieldsRegexp = require("./defaultFieldsRegexp"); + +/** + * Parse Database results + * ============================================================================== + * @description this function takes a database results array gotten from a DB handler + * function, decrypts encrypted fields, and returns an updated array with no encrypted + * fields + * + * @param {object} params - Single object params + * @param {any[]} params.unparsedResults - Array of data objects containing Fields(keys) + * and corresponding values of the fields(values) + * @param {import("../../types").DSQL_TableSchemaType} [params.tableSchema] - Table schema + * @returns {Promise} + */ +module.exports = async function parseDbResults({ + unparsedResults, + tableSchema, +}) { + /** + * Declare variables + * + * @description Declare "results" variable + */ + let parsedResults = []; + + try { + /** + * Declare variables + * + * @description Declare "results" variable + */ + for (let pr = 0; pr < unparsedResults.length; pr++) { + let result = unparsedResults[pr]; + + let resultFieldNames = Object.keys(result); + + for (let i = 0; i < resultFieldNames.length; i++) { + const resultFieldName = resultFieldNames[i]; + let resultFieldSchema = tableSchema?.fields[i]; + + if (resultFieldName?.match(defaultFieldsRegexp)) { + continue; + } + + let value = result[resultFieldName]; + + if (typeof value !== "number" && !value) { + // parsedResults.push(result); + continue; + } + + if (resultFieldSchema?.encrypted) { + if (value?.match(/./)) { + result[resultFieldName] = decrypt(value); + } + } + } + + parsedResults.push(result); + } + + /** + * Declare variables + * + * @description Declare "results" variable + */ + return parsedResults; + } catch (/** @type {any} */ error) { + console.log("ERROR in parseDbResults Function =>", error.message); + return unparsedResults; + } +}; diff --git a/package-shared/functions/backend/passwordHash.js b/package-shared/functions/backend/passwordHash.js new file mode 100644 index 0000000..75b9611 --- /dev/null +++ b/package-shared/functions/backend/passwordHash.js @@ -0,0 +1,31 @@ +// @ts-check + +const { createHmac } = require("crypto"); +// + +/** + * # Password Hash function + * @param {string} password + * @returns + */ +function hashPassword(password) { + const hmac = createHmac( + "sha512", + process.env.DSQL_ENCRYPTION_PASSWORD || "" + ); + hmac.update(password); + let hashed = hmac.digest("base64"); + return hashed; +} + +exports.hashPassword = hashPassword; + +// export const comparePasswords = async (password) => { +// const hmac = createHmac("sha512", process.env.DSQL_ENCRYPTION_PASSWORD); +// hmac.update(password); +// let hashed = hmac.digest("base64"); + +// let dbPass = await global.DB_HANDLER(`SELECT * FROM users WHERE password = '${hashed}'`); +// console.log(dbPass); +// return dbPass; +// }; diff --git a/package-shared/functions/backend/serverError.js b/package-shared/functions/backend/serverError.js new file mode 100644 index 0000000..2f7302e --- /dev/null +++ b/package-shared/functions/backend/serverError.js @@ -0,0 +1,55 @@ +// @ts-check + +/** + * ============================================================================== + * Imports + * ============================================================================== + */ +const fs = require("fs"); +// const handleNodemailer = require("./handleNodemailer"); + +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ + +/** + * ============================================================================== + * Main Function + * ============================================================================== + * @param {{ + * user?: { id?: number | string, first_name?: string, last_name?: string, email?: string } & *, + * message: string, + * component?: string, + * noMail?: boolean, + * }} params - user id + * + * @returns {Promise} + */ +module.exports = async function serverError({ + user, + message, + component, + noMail, +}) { + const log = `🚀 SERVER ERROR ===========================\nUser Id: ${ + user?.id + }\nUser Name: ${user?.first_name} ${user?.last_name}\nUser Email: ${ + user?.email + }\nError Message: ${message}\nComponent: ${component}\nDate: ${Date()}\n========================================`; + + if (!fs.existsSync(`./.tmp/error.log`)) { + fs.writeFileSync(`./.tmp/error.log`, "", "utf-8"); + } + + const initialText = fs.readFileSync(`./.tmp/error.log`, "utf-8"); + + fs.writeFileSync(`./.tmp/error.log`, log); + fs.appendFileSync(`./.tmp/error.log`, `\n\n\n\n\n${initialText}`); +}; + +//////////////////////////////////////// +//////////////////////////////////////// +//////////////////////////////////////// diff --git a/package-shared/functions/backend/suSocketAuth.js b/package-shared/functions/backend/suSocketAuth.js new file mode 100644 index 0000000..1ceaf8c --- /dev/null +++ b/package-shared/functions/backend/suSocketAuth.js @@ -0,0 +1,38 @@ +// @ts-check + +const { IncomingMessage } = require("http"); +const decrypt = require("./decrypt"); +const parseCookies = require("../../../utils/functions/parseCookies"); + +/** + * @async + * @param {IncomingMessage} req - https request object + * + * @returns {Promise<({ email: string, password: string, authKey: string, logged_in_status: boolean, date: number } | null)>} + */ +module.exports = async function (req) { + const cookies = parseCookies({ request: req }); + /** ********************* Check for existence of required cookie */ + if (!cookies?.datasquirelSuAdminUserAuthKey) { + return null; + } + + /** ********************* Grab the payload */ + let userPayload = decrypt(cookies.datasquirelSuAdminUserAuthKey); + + /** ********************* Return if no payload */ + if (!userPayload) return null; + + /** ********************* Parse the payload */ + let userObject = JSON.parse(userPayload); + + if (userObject.password !== process.env.DSQL_USER_KEY) return null; + if (userObject.authKey !== process.env.DSQL_SPECIAL_KEY) return null; + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + /** ********************* return user object */ + return userObject; +}; diff --git a/package-shared/functions/backend/varDatabaseDbHandler.js b/package-shared/functions/backend/varDatabaseDbHandler.js new file mode 100644 index 0000000..0492068 --- /dev/null +++ b/package-shared/functions/backend/varDatabaseDbHandler.js @@ -0,0 +1,116 @@ +// @ts-check + +const fs = require("fs"); +const parseDbResults = require("./parseDbResults"); +const serverError = require("./serverError"); +const DB_HANDLER = require("../../utils/backend/global-db/DB_HANDLER"); +const DSQL_USER_DB_HANDLER = require("../../utils/backend/global-db/DSQL_USER_DB_HANDLER"); + +/** + * DB handler for specific database + * ============================================================================== + * @async + * @param {object} params - Single object params + * @param {string} params.queryString - SQL string + * @param {*[]} [params.queryValuesArray] - Values Array + * @param {string} [params.database] - Database name + * @param {import("../../types").DSQL_TableSchemaType} [params.tableSchema] - Table schema + * @returns {Promise} + */ +module.exports = async function varDatabaseDbHandler({ + queryString, + queryValuesArray, + database, + tableSchema, +}) { + /** + * Declare variables + * + * @description Declare "results" variable + */ + const isMaster = database?.match(/^datasquirel$/) ? true : false; + + /** @type {any} */ + const FINAL_DB_HANDLER = isMaster ? DB_HANDLER : DSQL_USER_DB_HANDLER; + + let results; + + /** + * Fetch from db + * + * @description Fetch data from db if no cache + */ + try { + if ( + queryString && + queryValuesArray && + Array.isArray(queryValuesArray) && + queryValuesArray[0] + ) { + results = isMaster + ? await FINAL_DB_HANDLER(queryString, queryValuesArray) + : await FINAL_DB_HANDLER({ + paradigm: "Full Access", + database, + queryString, + queryValues: queryValuesArray, + }); + } else { + results = isMaster + ? await FINAL_DB_HANDLER(queryString) + : await FINAL_DB_HANDLER({ + paradigm: "Full Access", + database, + queryString, + }); + } + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + } catch (/** @type {any} */ error) { + serverError({ + component: "varDatabaseDbHandler/lines-29-32", + message: error.message, + }); + } + + /** + * Return results + * + * @description Return results add to cache if "req" param is passed + */ + if (results && tableSchema) { + try { + const unparsedResults = results; + const parsedResults = await parseDbResults({ + unparsedResults: unparsedResults, + tableSchema: tableSchema, + }); + return parsedResults; + } catch (/** @type {any} */ error) { + console.log( + "\x1b[31mvarDatabaseDbHandler ERROR\x1b[0m =>", + database, + error + ); + serverError({ + component: "varDatabaseDbHandler/lines-52-53", + message: error.message, + }); + return null; + } + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + } else if (results) { + return results; + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + } else { + return null; + } +}; diff --git a/package-shared/functions/backend/varReadOnlyDatabaseDbHandler.js b/package-shared/functions/backend/varReadOnlyDatabaseDbHandler.js new file mode 100644 index 0000000..6b8d9e5 --- /dev/null +++ b/package-shared/functions/backend/varReadOnlyDatabaseDbHandler.js @@ -0,0 +1,74 @@ +// @ts-check + +const fs = require("fs"); +const serverError = require("./serverError"); +const parseDbResults = require("./parseDbResults"); +const DSQL_USER_DB_HANDLER = require("../../utils/backend/global-db/DSQL_USER_DB_HANDLER"); + +/** + * + * @param {object} param0 + * @param {string} param0.queryString + * @param {string} param0.database + * @param {string[]} [param0.queryValuesArray] + * @param {import("../../types").DSQL_TableSchemaType} [param0.tableSchema] + * @returns + */ +module.exports = async function varReadOnlyDatabaseDbHandler({ + queryString, + database, + queryValuesArray, + tableSchema, +}) { + /** + * Declare variables + * + * @description Declare "results" variable + */ + let results; + + /** + * Fetch from db + * + * @description Fetch data from db if no cache + */ + try { + results = await DSQL_USER_DB_HANDLER({ + paradigm: "Read Only", + database, + queryString, + queryValues: queryValuesArray, + }); + + //////////////////////////////////////// + } catch (/** @type {any} */ error) { + //////////////////////////////////////// + + serverError({ + component: "varReadOnlyDatabaseDbHandler", + message: error.message, + noMail: true, + }); + + /** + * Return error + */ + return error.message; + } + + /** + * Return results + * + * @description Return results add to cache if "req" param is passed + */ + if (results) { + const unparsedResults = results; + const parsedResults = await parseDbResults({ + unparsedResults: unparsedResults, + tableSchema: tableSchema, + }); + return parsedResults; + } else { + return null; + } +}; diff --git a/package-shared/types/index.d.ts b/package-shared/types/index.d.ts index cb4019a..3793567 100644 --- a/package-shared/types/index.d.ts +++ b/package-shared/types/index.d.ts @@ -1,5 +1,6 @@ import http from "http"; +import { Editor } from "tinymce"; export type DSQL_DatabaseFullName = string; //////////////////////////////////////////////////////////////////////////////// @@ -139,6 +140,7 @@ export interface DSQL_MYSQL_FOREIGN_KEYS_Type { //////////////////////////////////////// export interface DSQL_MYSQL_user_databases_Type { + id: number; user_id: number; db_full_name: string; db_name: string; @@ -147,6 +149,17 @@ export interface DSQL_MYSQL_user_databases_Type { db_description: string; active_clone: number; active_clone_parent_db: string; + remote_connected?: number; + remote_db_full_name?: string; + remote_connection_host?: string; + remote_connection_key?: string; + remote_connection_type?: string; + user_priviledge?: string; + date_created?: string; + image_thumbnail?: string; + first_name?: string; + last_name?: string; + email?: string; } export interface PackageUserLoginRequestBody { @@ -370,3 +383,716 @@ export interface PostInsertReturn { protocol41: boolean; changedRows: number; } + +export interface UserType { + id: number; + stripe_id?: string; + first_name: string; + last_name: string; + email: string; + bio?: string; + username?: string; + image: string; + image_thumbnail: string; + social_id?: string; + verification_status?: number; + social_platform?: string; + social_login?: number; + date?: number; + phone?: number | string; + csrf_k: string; + logged_in_status: boolean; +} + +export interface ApiKeyDef { + name: string; + scope: string; + date_created: string; + apiKeyPayload: string; +} + +export interface MetricsType { + dbCount: number; + tablesCount: number; + mediaCount: number; + apiKeysCount: number; +} + +export interface DashboardContextType { + user?: UserType; + databases?: DSQL_MYSQL_user_databases_Type[]; + setTargetDatabase?: React.Dispatch< + React.SetStateAction + >; + targetDatabase?: DSQL_MYSQL_user_databases_Type; + metrics?: MetricsType; +} + +export interface AddDbContextType { + user?: UserType; + databases?: DSQL_MYSQL_user_databases_Type[]; + dbImage?: string | null | ImageObjectType; + setDbImage?: React.Dispatch< + React.SetStateAction + >; + query?: any; + duplicateDb?: DSQL_MYSQL_user_databases_Type; +} + +export interface EditDbContextType { + user?: UserType; + database?: DSQL_MYSQL_user_databases_Type; + dbImage?: string | null | ImageObjectType; + setDbImage?: React.Dispatch< + React.SetStateAction + >; +} + +export interface RichTextEditorsRefArray { + fieldName: string; + ref: React.MutableRefObject; +} + +export interface JSONTextEditorsRefArray { + fieldName: string; + ref: React.MutableRefObject; +} + +export interface TableEntriesContextType { + user: UserType; + database: DSQL_MYSQL_user_databases_Type; + table: DSQL_TableSchemaType; + dbSchemaData: DSQL_DatabaseSchemaType[]; + entries: any[]; + targetEntry?: any; + setTargetEntry: React.Dispatch>; + richTextEditors: React.MutableRefObject; + jsonTextEditors: React.MutableRefObject; + query?: any; + confirmedDelegetedUser?: any; + activeEntries: any[] | null; + setActiveEntries: React.Dispatch>; + targetField: React.MutableRefObject; + searchTerm: React.MutableRefObject; + entriesCount: number; +} + +export interface AddEntryContextType { + user: UserType; + database: DSQL_MYSQL_user_databases_Type; + table: DSQL_TableSchemaType; + dbSchemaData: DSQL_DatabaseSchemaType[]; + richTextEditors: React.MutableRefObject; + jsonTextEditors: React.MutableRefObject; + query: any; + duplicateEntry?: any; + confirmedDelegetedUser: any; +} + +export interface UserDatabasesContextType { + user: UserType; + users: any[]; + targetUser: any; + setTargetUser: React.Dispatch>; + databases: DSQL_MYSQL_user_databases_Type[]; +} + +export interface SettingsPageContextType { + user: UserType; + image: any; + setImage: React.Dispatch>; + activeUser: any; +} + +export interface MediaFolderPageContextType { + user: UserType; + media: any[]; + targetMedia: any; + setTargetMedia: React.Dispatch>; + folders: any[]; + query: any; + staticHost: string; + folder: string; +} + +export interface TablesContextType { + user: UserType; + database: DSQL_MYSQL_user_databases_Type; + tables: MYSQL_user_database_tables_table_def[]; + targetTable: MYSQL_user_database_tables_table_def | null; + setTargetTable: React.Dispatch< + React.SetStateAction + >; + query: any; + confirmedDelegetedUser: any; +} + +export interface EditTableContextType { + user: UserType; + database: DSQL_MYSQL_user_databases_Type; + table: DSQL_TableSchemaType; + tableFields: DSQL_FieldSchemaType[]; + setTableFields: React.Dispatch< + React.SetStateAction + >; + targetField: DSQL_FieldSchemaType | null; + setTargetField: React.Dispatch< + React.SetStateAction + >; + pageRefresh: number; + setPageRefresh: React.Dispatch>; + refreshFieldsListRef: React.MutableRefObject< + React.Dispatch> | undefined + >; + dbSchemaData: DSQL_DatabaseSchemaType[]; + query: any; + confirmedDelegetedUser: any; +} + +export interface SingleDatabaseContextType { + user: UserType; + database: DSQL_MYSQL_user_databases_Type; + tables: MYSQL_user_database_tables_table_def[]; + targetTable: MYSQL_user_database_tables_table_def | null; + setTargetTable: React.Dispatch< + React.SetStateAction + >; + query: any; + confirmedDelegetedUser: any; +} + +export interface ApiKeysContextType { + user: UserType; + apiKeys: any[]; + setApiKeys: React.Dispatch>; + targetApiKey: any | null; + setTargetApiKey: React.Dispatch>; + newApiKey: any | null; + setNewApiKey: React.Dispatch>; +} + +export interface LoginFormContextType { + user?: UserType | null; + loading: boolean; + setLoading: React.Dispatch>; + alert: string | boolean; + setAlert: React.Dispatch>; +} + +export interface CreateAccountContextType { + user?: UserType | null; + query: CreateAccountQueryType; + invitingUser: any; +} + +export interface CreateAccountQueryType { + invite?: number; + database_access?: string; + priviledge?: string; + email?: string; +} + +export interface DocsAsidePageObject { + id: number; + title: string; + slug: string; + parent_id?: number; + level?: number; +} + +export interface AllUserUsersContextType { + user: UserType; + users: MYSQL_delegated_users_table_def[]; + targetUser: MYSQL_user_users_table_def | null; + setTargetUser: React.Dispatch< + React.SetStateAction + >; + databases: DSQL_MYSQL_user_databases_Type[]; + pendingInvitations: MYSQL_invitations_table_def[]; + pendingInvitationsReceived: any[]; + adminUsers: any[]; + invitedAccounts: any[]; +} + +export interface AddSocialLoginContextType { + user: UserType; + database: DSQL_MYSQL_user_databases_Type; + query: any; + socialLogins: SocialLoginObjectType[]; +} + +export interface DelegatedDbContextType { + user: UserType; + users: MYSQL_user_users_table_def[]; + targetUser: MYSQL_user_users_table_def | null; + setTargetUser: React.Dispatch< + React.SetStateAction + >; + database: DSQL_MYSQL_user_databases_Type; +} + +export interface AddUserUserContextType { + user: UserType; + database: DSQL_MYSQL_user_databases_Type; + table: DSQL_TableSchemaType; + query: any; + confirmedDelegetedUser: any; +} + +export interface UserUsersContextType { + user: UserType; + users: MYSQL_user_users_table_def[]; + targetUser: MYSQL_user_users_table_def; + setTargetUser: React.Dispatch< + React.SetStateAction + >; + database: DSQL_MYSQL_user_databases_Type; + table: DSQL_TableSchemaType; + dbSchemaData: DSQL_DatabaseSchemaType[]; + query: any; + confirmedDelegetedUser: any; +} + +export interface DatabaseSingleUserContextType { + user: UserType; + database: DSQL_MYSQL_user_databases_Type; + singleUser: MYSQL_user_users_table_def; + table: DSQL_TableSchemaType; + dbSchemaData: DSQL_DatabaseSchemaType[]; + query: any; + confirmedDelegetedUser: any; +} + +export interface SingleUserUserContextType { + user: UserType; + singleUser: MYSQL_user_users_table_def; +} + +export interface AddUserContextType { + user: UserType; + users: MYSQL_delegated_users_table_def[]; + databases: DSQL_MYSQL_user_databases_Type[]; + query: any; +} + +export interface MediaContextType { + user: UserType; + media: MYSQL_user_media_table_def[]; + targetMedia: MYSQL_user_media_table_def | null; + setTargetMedia: React.Dispatch< + React.SetStateAction + >; + folders: string[]; + staticHost: string; +} + +export interface MediaSubFolderContextType { + user: UserType; + media: MYSQL_user_media_table_def[]; + targetMedia: MYSQL_user_media_table_def | null; + setTargetMedia: React.Dispatch< + React.SetStateAction + >; + folders: string[]; + query: any; + folder: string; + staticHost: string; +} + +export interface FieldsContextType { + user: UserType; + database: DSQL_MYSQL_user_databases_Type; + table: DSQL_TableSchemaType; + dbSchemaData: DSQL_DatabaseSchemaType[]; + targetField: DSQL_FieldSchemaType | null; + setTargetField: React.Dispatch< + React.SetStateAction + >; + refreshFieldsListRef: React.MutableRefObject< + React.Dispatch> | undefined + >; + tableFields: DSQL_FieldSchemaType[]; + setTableFields: React.Dispatch< + React.SetStateAction + >; + updateTableAfterFieldsUpdateFunction: () => void; + query: any; + confirmedDelegetedUser: any; +} + +export interface SingleTableContextType { + user: UserType; + database: DSQL_MYSQL_user_databases_Type; + table: DSQL_TableSchemaType; + tableRecord: MYSQL_user_database_tables_table_def; + tableFields: DSQL_FieldSchemaType[]; + setTableFields: React.Dispatch< + React.SetStateAction + >; + tableIndexes: DSQL_IndexSchemaType[]; + setTableIndexes: React.Dispatch< + React.SetStateAction + >; + dbSchemaData: DSQL_DatabaseSchemaType[]; + entries: any[]; + targetEntry: any; + setTargetEntry: React.Dispatch>; + richTextEditors: React.MutableRefObject; + jsonTextEditors: React.MutableRefObject; + query: any; + confirmedDelegetedUser: any; + targetField: DSQL_FieldSchemaType | null; + setTargetField: React.Dispatch< + React.SetStateAction + >; + refreshFieldsListRef: React.MutableRefObject< + React.Dispatch> + >; + updateTableAfterFieldsUpdateFunction: () => void; + entriesCount: number; +} + +export interface SingleEntryContextType { + user: UserType; + database: DSQL_MYSQL_user_databases_Type; + table: DSQL_TableSchemaType; + dbSchemaData: DSQL_DatabaseSchemaType[]; + entry: any; + targetEntry: any; + setTargetEntry: React.Dispatch>; + richTextEditors: React.MutableRefObject; + jsonTextEditors: React.MutableRefObject; + query: any; + confirmedDelegetedUser: any; + prevEntry: any; + nextEntry: any; +} + +export interface UserSchemaContextType { + user: UserType; + dbSchemaData: DSQL_DatabaseSchemaType[]; +} + +export interface ConnectContextType { + user: UserType; + query: any; + mariadbUserCred: MariaDBUserCredType; + mariadbUsers: MYSQL_mariadb_users_table_def[]; + targetMariadbUser: MYSQL_mariadb_users_table_def | null; + setTargetMariadbUser: React.Dispatch< + React.SetStateAction + >; + refresh: number; + setRefresh: React.Dispatch>; +} + +export interface MYSQL_mariadb_users_table_def { + id?: number; + user_id?: number; + username?: string; + host?: string; + password?: string; + primary?: number; + grants?: string; + date_created?: string; + date_created_code?: number; + date_created_timestamp?: string; + date_updated?: string; + date_updated_code?: number; + date_updated_timestamp?: string; +} + +export interface DbContextType { + user?: UserType; + databases?: DSQL_MYSQL_user_databases_Type[]; + targetDatabase?: DSQL_MYSQL_user_databases_Type; + setTargetDatabase?: React.Dispatch< + React.SetStateAction + >; +} + +export interface MariaDBUserCredType { + mariadb_user?: string; + mariadb_host?: string; + mariadb_pass?: string; +} + +export interface AddTableContextType { + user: UserType; + dbSchemaData: DSQL_DatabaseSchemaType[]; + database: DSQL_MYSQL_user_databases_Type; + tables: DSQL_TableSchemaType[]; + tableFields: DSQL_FieldSchemaType[]; + setTableFields: React.Dispatch< + React.SetStateAction + >; + targetField: DSQL_FieldSchemaType | null; + setTargetField: React.Dispatch< + React.SetStateAction + >; + pageRefresh: number | null; + setPageRefresh: React.Dispatch>; + refreshFieldsListRef: React.MutableRefObject< + React.Dispatch> + >; + query: any; +} + +export interface DbSchemaContextType { + user: UserType; + database: DSQL_MYSQL_user_databases_Type; + dbImage: string; + setDbImage: React.Dispatch>; + dbSchemaData: DSQL_DatabaseSchemaType[]; + tables: any[]; +} + +export interface DbShellContextType { + user?: UserType; + database?: DSQL_MYSQL_user_databases_Type; + dbImage?: string; + setDbImage?: React.Dispatch>; + dbSchemaData?: DSQL_DatabaseSchemaType[]; + tables?: any[]; +} + +export interface DbConnectContextType { + user: UserType; + database: DSQL_MYSQL_user_databases_Type; + targetDbSchema: DSQL_DatabaseSchemaType; + query: any; +} + +export interface ImageObjectType { + imageName?: string; + mimeType?: string; + imageSize?: number; + private?: boolean; + imageBase64?: string; + imageBase64Full?: string; +} + +export interface FileObjectType { + fileName?: string; + private?: boolean; + fileType?: string; + fileSize?: number; + fileBase64?: string; + fileBase64Full?: string; +} + +export interface SocialLoginObjectType { + platform?: string; + paradigm?: string; + clientId?: string; + clientSecret?: string; + callbackUrl?: string; + domain1?: string; + domain2?: string; + domain3?: string; +} + +export interface DbConnectType { + url: string; + key: string; + database: DSQL_MYSQL_user_databases_Type; + dbSchema: DSQL_DatabaseSchemaType; + type: "pull" | "push"; + remoteDbs?: DSQL_DatabaseSchemaType[]; + targetDb?: DSQL_DatabaseSchemaType; +} + +export interface MYSQL_MediaType { + id?: number; + user_id?: number; + media_name?: string; + folder?: string; + media_url?: string; + media_thumbnail_url?: string; + media_type?: string; + width?: string; + height?: string; + size?: string; + private?: string; +} + +export interface UserFileObject { + title?: string; + path?: string; + data?: string; +} + +export interface UserFileObject2 { + type?: string; + name?: string; + root?: string; + content?: UserFileObject2[]; +} + +export interface MYSQL_user_users_table_def { + id?: number; + user_id?: number; + invited_user_id?: number; + database?: string; + database_access?: string; + first_name?: string; + last_name?: string; + email?: string; + username?: string; + password?: string; + phone?: string; + user_type?: string; + user_priviledge?: string; + image?: string; + image_thumbnail?: string; + city?: string; + state?: string; + country?: string; + zip_code?: string; + address?: string; + social_login?: number; + social_platform?: string; + social_id?: string; + verification_status?: number; + more_user_data?: string; + date_created?: string; + date_created_code?: number; + date_created_timestamp?: string; + date_updated?: string; + date_updated_code?: number; + date_updated_timestamp?: string; + inviteeFirstName?: string; + inviteeLastName?: string; + inviteeEmail?: string; + inviteeImage?: string; +} + +export interface MYSQL_user_database_tables_table_def { + id?: number; + user_id?: number; + db_id?: number; + db_slug?: string; + table_name?: string; + table_slug?: string; + table_description?: string; + child_table?: number; + child_table_parent_database?: string; + child_table_parent_table?: string; + date_created?: string; + date_created_code?: number; + date_created_timestamp?: string; + date_updated?: string; + date_updated_code?: number; + date_updated_timestamp?: string; +} + +export interface MYSQL_user_media_table_def { + id?: number; + user_id?: number; + media_name?: string; + folder?: string; + media_url?: string; + media_thumbnail_url?: string; + media_path?: string; + media_thumbnail_path?: string; + media_type?: string; + width?: number; + height?: number; + size?: number; + private?: number; + date_created?: string; + date_created_code?: number; + date_created_timestamp?: string; + date_updated?: string; + date_updated_code?: number; + date_updated_timestamp?: string; +} + +export interface MYSQL_delegated_users_table_def { + id?: number; + user_id?: number; + delegated_user_id?: number; + permissions?: string; + permission_level_code?: number; + date_created?: string; + date_created_code?: number; + date_created_timestamp?: string; + date_updated?: string; + date_updated_code?: number; + date_updated_timestamp?: string; +} + +export interface MYSQL_invitations_table_def { + id?: number; + inviting_user_id?: number; + invited_user_email?: string; + invitation_status?: string; + database_access?: string; + priviledge?: string; + db_tables_data?: string; + date_created?: string; + date_created_code?: number; + date_created_timestamp?: string; + date_updated?: string; + date_updated_code?: number; + date_updated_timestamp?: string; +} + +export interface MYSQL_docs_pages_table_def { + id?: number; + title?: string; + slug?: string; + description?: string; + content?: string; + text_content?: string; + level?: number; + page_order?: number; + parent_id?: number; + date_created?: string; + date_created_code?: number; + date_created_timestamp?: string; + date_updated?: string; + date_updated_code?: number; + date_updated_timestamp?: string; +} + +export interface MYSQL_delegated_user_tables_table_def { + id?: number; + delegated_user_id?: number; + root_user_id?: number; + database?: string; + table?: string; + priviledge?: string; + date_created?: string; + date_created_code?: number; + date_created_timestamp?: string; + date_updated?: string; + date_updated_code?: number; + date_updated_timestamp?: string; +} + +export type ApiKeyObject = { + user_id: string | number; + full_access?: boolean; + sign: string; + date_code: number; + target_database?: string; + target_table?: string; +}; + +export type AddApiKeyRequestBody = { + api_key_name: string; + api_key_slug: string; + api_key_scope?: "fullAccess" | "readOnly"; + target_database?: string; + target_table?: string; +}; + +export type CheckApiCredentialsFn = ( + param: CheckApiCredentialsFnParam +) => ApiKeyObject | null | undefined; +export type CheckApiCredentialsFnParam = { + key: string; + database?: string; + table?: string; +}; diff --git a/package-shared/types/index.js b/package-shared/types/index.js index 7002157..5eb7a33 100644 --- a/package-shared/types/index.js +++ b/package-shared/types/index.js @@ -153,6 +153,7 @@ /** * @typedef {object} DSQL_MYSQL_user_databases_Type + * @property {number} id * @property {number} user_id - User Id * @property {string} db_full_name - Database full name => eg. (dataasquirel_user_2_new_database) * @property {string} db_name - Database name with spaces => eg. (New Database) @@ -161,6 +162,15 @@ * @property {string} db_description - Database description * @property {number} active_clone - is Database active clone => 0 or 1 * @property {string} active_clone_parent_db - Database parent db full name => eg. "datasquirel_user_7_wexculture" + * @property {number} [remote_connected] + * @property {string} [remote_db_full_name] + * @property {string} [remote_connection_host] + * @property {string} [user_priviledge] + * @property {string} [date_created] + * @property {string} [image_thumbnail] + * @property {string} [first_name] + * @property {string} [last_name] + * @property {string} [email] */ /** @@ -426,3 +436,731 @@ const http = require("http"); * @property {boolean} protocol41 * @property {number} changedRows */ + +// @ts-check + +const { Editor } = require("tinymce"); + +/** + * @typedef {object} UserType + * @property {number} id - user id (number) + * @property {string} [stripe_id] - Stripe ID for payments + * @property {string} first_name - User First Name + * @property {string} last_name - User Last Name + * @property {string} email - User Email Address + * @property {string} [bio] - User Description HTML + * @property {string} [username] - User Username + * @property {string} image - User Full Image + * @property {string} image_thumbnail - User Image Thumbnail + * @property {string} [social_id] - User Social id if available + * @property {number} [verification_status] - 0 or 1 or 2 + * @property {string} [social_platform] - Google or Facebook or Github + * @property {number} [social_login] - 0 or 1 => is this user a social user(1) or not(0) + * @property {number} [date] - Creation Date + * @property {number | string} [phone] + * @property {string} csrf_k - CSRF key + * @property {boolean} logged_in_status - Is user logged in or not + */ + +/** + * @typedef {object} ApiKeyDef + * @property {string} name + * @property {string} scope + * @property {string} date_created + * @property {string} apiKeyPayload + */ + +/** + * @typedef {object} MetricsType + * @property {number} dbCount + * @property {number} tablesCount + * @property {number} mediaCount + * @property {number} apiKeysCount + */ + +/** + * @typedef {object} DashboardContextType + * @property {UserType} [user] + * @property {DSQL_MYSQL_user_databases_Type[]} [databases] + * @property {React.Dispatch>} [setTargetDatabase] + * @property {DSQL_MYSQL_user_databases_Type} [targetDatabase] + * @property {MetricsType} [metrics] + */ + +/** + * @typedef {object} AddDbContextType + * @property {UserType} [user] + * @property {DSQL_MYSQL_user_databases_Type[]} [databases] + * @property {string | null | ImageObjectType} [dbImage] + * @property {React.Dispatch>} [setDbImage] + * @property {*} [query] + * @property {DSQL_MYSQL_user_databases_Type} [duplicateDb] + */ + +/** + * @typedef {object} EditDbContextType + * @property {UserType} [user] + * @property {DSQL_MYSQL_user_databases_Type} [database] + * @property {string | null | ImageObjectType} [dbImage] + * @property {React.Dispatch>} [setDbImage] + */ + +/** + * @typedef {object} RichTextEditorsRefArray + * @property {string} fieldName + * @property {React.MutableRefObject} ref + */ + +/** + * @typedef {object} JSONTextEditorsRefArray + * @property {string} fieldName + * @property {React.MutableRefObject} ref + */ + +/** + * @typedef {object} TableEntriesContextType + * @property {UserType} user + * @property {DSQL_MYSQL_user_databases_Type} database + * @property {DSQL_TableSchemaType} table + * @property {DSQL_DatabaseSchemaType[]} dbSchemaData + * @property {any[]} entries + * @property {any} [targetEntry] + * @property {React.Dispatch>} setTargetEntry + * @property {React.MutableRefObject} richTextEditors + * @property {React.MutableRefObject} jsonTextEditors + * @property {any} [query] + * @property {any} [confirmedDelegetedUser] + * @property {any[] | null} activeEntries + * @property {React.Dispatch>} setActiveEntries + * @property {React.MutableRefObject} targetField + * @property {React.MutableRefObject} searchTerm + * @property {number} entriesCount + */ + +/** + * @typedef {object} AddEntryContextType + * @property {UserType} user + * @property {DSQL_MYSQL_user_databases_Type} database + * @property {DSQL_TableSchemaType} table + * @property {DSQL_DatabaseSchemaType[]} dbSchemaData + * @property {React.MutableRefObject} richTextEditors + * @property {React.MutableRefObject} jsonTextEditors + * @property {any} query + * @property {any} [duplicateEntry] + * @property {any} confirmedDelegetedUser + */ + +/** + * @typedef {object} UserDatabasesContextType + * @property {UserType} user + * @property {any[]} users + * @property {any} targetUser + * @property {React.Dispatch>} setTargetUser + * @property {DSQL_MYSQL_user_databases_Type[]} databases + */ + +/** + * @typedef {object} SettingsPageContextType + * @property {UserType} user + * @property {any} image + * @property {React.Dispatch>} setImage + * @property {any} activeUser + */ + +/** + * @typedef {object} MediaFolderPageContextType + * @property {UserType} user + * @property {any[]} media + * @property {any} targetMedia + * @property {React.Dispatch>} setTargetMedia + * @property {any[]} folders + * @property {any} query + * @property {string} staticHost + * @property {string} folder + */ + +/** + * @typedef {object} TablesContextType + * @property {UserType} user + * @property {DSQL_MYSQL_user_databases_Type} database + * @property {MYSQL_user_database_tables_table_def[]} tables + * @property {MYSQL_user_database_tables_table_def | null} targetTable + * @property {React.Dispatch>} setTargetTable + * @property {any} query + * @property {any} confirmedDelegetedUser + */ + +/** + * @typedef {object} EditTableContextType + * @property {UserType} user + * @property {DSQL_MYSQL_user_databases_Type} database + * @property {DSQL_TableSchemaType} table + * @property {DSQL_FieldSchemaType[]} tableFields + * @property {React.Dispatch>} setTableFields + * @property {DSQL_FieldSchemaType | null} targetField + * @property {React.Dispatch>} setTargetField + * @property {number} pageRefresh + * @property {React.Dispatch>} setPageRefresh + * @property {React.MutableRefObject> | undefined>} refreshFieldsListRef + * @property {DSQL_DatabaseSchemaType[]} dbSchemaData + * @property {any} query + * @property {any} confirmedDelegetedUser + */ + +/** + * @typedef {object} SingleDatabaseContextType + * @property {UserType} user + * @property {DSQL_MYSQL_user_databases_Type} database + * @property {MYSQL_user_database_tables_table_def[]} tables + * @property {MYSQL_user_database_tables_table_def | null} targetTable + * @property {React.Dispatch>} setTargetTable + * @property {any} query + * @property {any} confirmedDelegetedUser + */ + +/** + * @typedef {object} ApiKeysContextType + * @property {UserType} user + * @property {any[]} apiKeys + * @property {React.Dispatch>} setApiKeys + * @property {any | null} targetApiKey + * @property {React.Dispatch>} setTargetApiKey + * @property {any | null} newApiKey + * @property {React.Dispatch>} setNewApiKey + */ + +/** + * @typedef {object} LoginFormContextType + * @property {UserType | null} [user] + * @property {boolean} loading + * @property {React.Dispatch>} setLoading + * @property {string | boolean} alert + * @property {React.Dispatch>} setAlert + */ + +/** + * @typedef {object} CreateAccountContextType + * @property {UserType | null} [user] + * @property {CreateAccountQueryType} query + * @property {any} invitingUser + */ + +/** + * @typedef {object} CreateAccountQueryType + * @property {number} [invite] + * @property {string} [database_access] + * @property {string} [priviledge] + * @property {string} [email] + */ + +/** + * @typedef {object} DocsAsidePageObject + * @property {number} id + * @property {string} title + * @property {string} slug + * @property {number} [parent_id] + * @property {number} [level] + */ + +/** + * @typedef {object} AllUserUsersContextType + * @property {UserType} user + * @property {MYSQL_delegated_users_table_def[]} users + * @property {MYSQL_user_users_table_def | null} targetUser + * @property {React.Dispatch>} setTargetUser + * @property {DSQL_MYSQL_user_databases_Type[]} databases + * @property {MYSQL_invitations_table_def[]} pendingInvitations + * @property {any[]} pendingInvitationsReceived + * @property {any[]} adminUsers + * @property {any[]} invitedAccounts + */ + +/** + * @typedef {object} AddSocialLoginContextType + * @property {UserType} user + * @property {DSQL_MYSQL_user_databases_Type} database + * @property {any} query + * @property {SocialLoginObjectType[]} socialLogins + */ + +/** + * @typedef {object} DelegatedDbContextType + * @property {UserType} user + * @property {MYSQL_user_users_table_def[]} users + * @property {MYSQL_user_users_table_def | null} targetUser + * @property {React.Dispatch>} setTargetUser + * @property {DSQL_MYSQL_user_databases_Type} database + */ + +/** + * @typedef {object} AddUserUserContextType + * @property {UserType} user + * @property {DSQL_MYSQL_user_databases_Type} database + * @property {DSQL_TableSchemaType} table + * @property {any} query + * @property {any} confirmedDelegetedUser + */ + +/** + * @typedef {object} UserUsersContextType + * @property {UserType} user + * @property {MYSQL_user_users_table_def[]} users + * @property {MYSQL_user_users_table_def} targetUser + * @property {React.Dispatch>} setTargetUser + * @property {DSQL_MYSQL_user_databases_Type} database + * @property {DSQL_TableSchemaType} table + * @property {DSQL_DatabaseSchemaType[]} dbSchemaData + * @property {any} query + * @property {any} confirmedDelegetedUser + */ + +/** + * @typedef {object} DatabaseSingleUserContextType + * @property {UserType} user + * @property {DSQL_MYSQL_user_databases_Type} database + * @property {MYSQL_user_users_table_def} singleUser + * @property {DSQL_TableSchemaType} table + * @property {DSQL_DatabaseSchemaType[]} dbSchemaData + * @property {any} query + * @property {any} confirmedDelegetedUser + */ + +/** + * @typedef {object} SingleUserUserContextType + * @property {UserType} user + * @property {MYSQL_user_users_table_def} singleUser + */ + +/** + * @typedef {object} AddUserContextType + * @property {UserType} user + * @property {MYSQL_delegated_users_table_def[]} users + * @property {DSQL_MYSQL_user_databases_Type[]} databases + * @property {any} query + */ + +/** + * @typedef {object} MediaContextType + * @property {UserType} user + * @property {MYSQL_user_media_table_def[]} media + * @property {MYSQL_user_media_table_def | null} targetMedia + * @property {React.Dispatch>} setTargetMedia + * @property {string[]} folders + * @property {string} staticHost + */ + +/** + * @typedef {object} MediaSubFolderContextType + * @property {UserType} user + * @property {MYSQL_user_media_table_def[]} media + * @property {MYSQL_user_media_table_def | null} targetMedia + * @property {React.Dispatch>} setTargetMedia + * @property {string[]} folders + * @property {any} query + * @property {string} folder + * @property {string} staticHost + */ + +/** + * @typedef {object} FieldsContextType + * @property {UserType} user + * @property {DSQL_MYSQL_user_databases_Type} database + * @property {DSQL_TableSchemaType} table + * @property {DSQL_DatabaseSchemaType[]} dbSchemaData + * @property {DSQL_FieldSchemaType | null} targetField + * @property {React.Dispatch>} setTargetField + * @property {React.MutableRefObject> | undefined>} refreshFieldsListRef + * @property {DSQL_FieldSchemaType[]} tableFields + * @property {React.Dispatch>} setTableFields + * @property {()=>void} updateTableAfterFieldsUpdateFunction + * @property {any} query + * @property {any} confirmedDelegetedUser + */ + +/** + * @typedef {object} SingleTableContextType + * @property {UserType} user + * @property {DSQL_MYSQL_user_databases_Type} database + * @property {DSQL_TableSchemaType} table + * @property {MYSQL_user_database_tables_table_def} tableRecord + * @property {DSQL_FieldSchemaType[]} tableFields + * @property {React.Dispatch>} setTableFields + * @property {DSQL_IndexSchemaType[]} tableIndexes + * @property {React.Dispatch>} setTableIndexes + * @property {DSQL_DatabaseSchemaType[]} dbSchemaData + * @property {any[]} entries + * @property {any} targetEntry + * @property {React.Dispatch>} setTargetEntry + * @property {React.MutableRefObject} richTextEditors + * @property {React.MutableRefObject} jsonTextEditors + * @property {any} query + * @property {any} confirmedDelegetedUser + * @property {DSQL_FieldSchemaType | null} targetField + * @property {React.Dispatch>} setTargetField + * @property {React.MutableRefObject>>} refreshFieldsListRef + * @property {()=>void} updateTableAfterFieldsUpdateFunction + * @property {number} entriesCount + */ + +/** + * @typedef {object} SingleEntryContextType + * @property {UserType} user + * @property {DSQL_MYSQL_user_databases_Type} database + * @property {DSQL_TableSchemaType} table + * @property {DSQL_DatabaseSchemaType[]} dbSchemaData + * @property {any} entry + * @property {any} targetEntry + * @property {React.Dispatch>} setTargetEntry + * @property {React.MutableRefObject} richTextEditors + * @property {React.MutableRefObject} jsonTextEditors + * @property {any} query + * @property {any} confirmedDelegetedUser + * @property {any} prevEntry + * @property {any} nextEntry + */ + +/** + * @typedef {object} UserSchemaContextType + * @property {UserType} user + * @property {DSQL_DatabaseSchemaType[]} dbSchemaData + */ + +/** + * @typedef {object} ConnectContextType + * @property {UserType} user + * @property {any} query + * @property {MariaDBUserCredType} mariadbUserCred + * @property {MYSQL_mariadb_users_table_def[]} mariadbUsers - All MariaDB Users including the primary User + * @property {MYSQL_mariadb_users_table_def | null} targetMariadbUser + * @property {React.Dispatch>} setTargetMariadbUser + * @property {number} refresh + * @property {React.Dispatch>} setRefresh + */ + +/** + * @typedef {object} MYSQL_mariadb_users_table_def + * @property {number} [id] - NULL=`NO` Key=`PRI` Default=`null` Extra=`auto_increment` + * @property {number} [user_id] - NULL=`NO` Key=`MUL` Default=`null` Extra=`` + * @property {string} [username] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {string} [host] - NULL=`YES` Key=`` Default=`%` Extra=`` + * @property {string} [password] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {number} [primary] - NULL=`YES` Key=`` Default=`0` Extra=`` + * @property {string} [grants] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [date_created] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {number} [date_created_code] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [date_created_timestamp] - NULL=`YES` Key=`` Default=`current_timestamp()` Extra=`` + * @property {string} [date_updated] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {number} [date_updated_code] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [date_updated_timestamp] - NULL=`YES` Key=`` Default=`current_timestamp()` Extra=`on update current_timestamp()` + */ + +/** + * @typedef {object} DbContextType + * @property {UserType} [user] + * @property {DSQL_MYSQL_user_databases_Type[]} [databases] + * @property {DSQL_MYSQL_user_databases_Type} [targetDatabase] + * @property {React.Dispatch>} [setTargetDatabase] + */ + +/** + * @typedef {object} MariaDBUserCredType + * @property {string} [mariadb_user] + * @property {string} [mariadb_host] + * @property {string} [mariadb_pass] + */ + +/** + * @typedef {object} AddTableContextType + * @property {UserType} user + * @property {DSQL_DatabaseSchemaType[]} dbSchemaData + * @property {DSQL_MYSQL_user_databases_Type} database + * @property {DSQL_TableSchemaType[]} tables + * @property {DSQL_FieldSchemaType[]} tableFields + * @property {React.Dispatch>} setTableFields + * @property {DSQL_FieldSchemaType | null} targetField + * @property {React.Dispatch>} setTargetField + * @property {number | null} pageRefresh + * @property {React.Dispatch>} setPageRefresh + * @property {React.MutableRefObject>>} refreshFieldsListRef + * @property {any} query + */ + +/** + * @typedef {object} DbSchemaContextType + * @property {UserType} user + * @property {DSQL_MYSQL_user_databases_Type} database + * @property {string} dbImage + * @property {React.Dispatch>} setDbImage + * @property {DSQL_DatabaseSchemaType[]} dbSchemaData + * @property {any[]} tables + */ + +/** + * @typedef {object} DbShellContextType + * @property {UserType} [user] + * @property {DSQL_MYSQL_user_databases_Type} [database] + * @property {string} [dbImage] + * @property {React.Dispatch>} [setDbImage] + * @property {DSQL_DatabaseSchemaType[]} [dbSchemaData] + * @property {any[]} [tables] + */ + +/** + * @typedef {object} DbConnectContextType + * @property {UserType} user + * @property {DSQL_MYSQL_user_databases_Type} database + * @property {DSQL_DatabaseSchemaType} targetDbSchema + * @property {any} query + */ + +/** + * @typedef {object} ImageObjectType + * @property {string} [imageName] + * @property {string} [mimeType] + * @property {number} [imageSize] + * @property {boolean} [private] + * @property {string} [imageBase64] + * @property {string} [imageBase64Full] + */ + +/** + * @typedef {object} FileObjectType + * @property {string} [fileName] + * @property {boolean} [private] + * @property {string} [fileType] + * @property {number} [fileSize] + * @property {string} [fileBase64] + * @property {string} [fileBase64Full] + */ + +/** + * @typedef {object} SocialLoginObjectType + * @property {string} [platform] + * @property {string} [paradigm] + * @property {string} [clientId] + * @property {string} [clientSecret] + * @property {string} [callbackUrl] + * @property {string} [domain1] + * @property {string} [domain2] + * @property {string} [domain3] + */ + +/** + * @typedef {object} DbConnectType + * @property {string} url - Remote URL + * @property {string} key - Full Access API key + * @property {DSQL_MYSQL_user_databases_Type} database - DSQL database entry + * @property {DSQL_DatabaseSchemaType} dbSchema - Database JSON schema + * @property {"pull" | "push"} type - Type of connection: "pull" or "push" + * @property {DSQL_DatabaseSchemaType[]} [remoteDbs] - All Databases Pulled from the remote + * @property {DSQL_DatabaseSchemaType} [targetDb] - The Target Database to be cloned + */ + +/** + * @typedef {object} MYSQL_MediaType + * @property {number} [id] + * @property {number} [user_id] + * @property {string} [media_name] + * @property {string} [folder] + * @property {string} [media_url] + * @property {string} [media_thumbnail_url] + * @property {string} [media_type] + * @property {string} [width] + * @property {string} [height] + * @property {string} [size] + * @property {string} [private] + */ + +/** + * @typedef {object} UserFileObject + * @property {string} [title] + * @property {string} [path] + * @property {string} [data] + */ + +/** + * @typedef {object} UserFileObject2 + * @property {string} [type] + * @property {string} [name] + * @property {string} [root] + * @property {UserFileObject2[]} [content] + */ + +/** + * @typedef {object} MYSQL_user_users_table_def + * @property {number} [id] - NULL=`NO` Key=`PRI` Default=`null` Extra=`auto_increment` + * @property {number} [user_id] - NULL=`NO` Key=`MUL` Default=`null` Extra=`` + * @property {number} [invited_user_id] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {string} [database] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [database_access] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [first_name] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {string} [last_name] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {string} [email] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {string} [username] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {string} [password] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [phone] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [user_type] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [user_priviledge] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [image] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [image_thumbnail] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [city] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [state] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [country] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [zip_code] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [address] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {number} [social_login] - NULL=`YES` Key=`` Default=`0` Extra=`` + * @property {string} [social_platform] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [social_id] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {number} [verification_status] - NULL=`YES` Key=`` Default=`0` Extra=`` + * @property {string} [more_user_data] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [date_created] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {number} [date_created_code] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {string} [date_created_timestamp] - NULL=`YES` Key=`` Default=`CURRENT_TIMESTAMP` Extra=`DEFAULT_GENERATED` + * @property {string} [date_updated] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {number} [date_updated_code] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {string} [date_updated_timestamp] - NULL=`YES` Key=`` Default=`CURRENT_TIMESTAMP` Extra=`DEFAULT_GENERATED on update CURRENT_TIMESTAMP` + * @property {string} [inviteeFirstName] - QUERY JOIN + * @property {string} [inviteeLastName] - QUERY JOIN + * @property {string} [inviteeEmail] - QUERY JOIN + * @property {string} [inviteeImage] - QUERY JOIN + */ + +/** + * @typedef {object} MYSQL_user_database_tables_table_def + * @property {number} [id] - NULL=`NO` Key=`PRI` Default=`null` Extra=`auto_increment` + * @property {number} [user_id] - NULL=`NO` Key=`MUL` Default=`null` Extra=`` + * @property {number} [db_id] - NULL=`NO` Key=`MUL` Default=`null` Extra=`` + * @property {string} [db_slug] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [table_name] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {string} [table_slug] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {string} [table_description] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {number} [child_table] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [child_table_parent_database] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [child_table_parent_table] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [date_created] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {number} [date_created_code] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {string} [date_created_timestamp] - NULL=`YES` Key=`` Default=`CURRENT_TIMESTAMP` Extra=`DEFAULT_GENERATED` + * @property {string} [date_updated] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {number} [date_updated_code] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {string} [date_updated_timestamp] - NULL=`YES` Key=`` Default=`CURRENT_TIMESTAMP` Extra=`DEFAULT_GENERATED on update CURRENT_TIMESTAMP` + */ + +/** + * @typedef {object} MYSQL_user_media_table_def + * @property {number} [id] - NULL=`NO` Key=`PRI` Default=`null` Extra=`auto_increment` + * @property {number} [user_id] - NULL=`NO` Key=`MUL` Default=`null` Extra=`` + * @property {string} [media_name] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [folder] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [media_url] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {string} [media_thumbnail_url] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [media_path] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {string} [media_thumbnail_path] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [media_type] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {number} [width] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {number} [height] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {number} [size] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {number} [private] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [date_created] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {number} [date_created_code] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {string} [date_created_timestamp] - NULL=`YES` Key=`` Default=`CURRENT_TIMESTAMP` Extra=`DEFAULT_GENERATED` + * @property {string} [date_updated] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {number} [date_updated_code] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {string} [date_updated_timestamp] - NULL=`YES` Key=`` Default=`CURRENT_TIMESTAMP` Extra=`DEFAULT_GENERATED on update CURRENT_TIMESTAMP` + */ + +/** + * @typedef {object} MYSQL_delegated_users_table_def + * @property {number} [id] - NULL=`NO` Key=`PRI` Default=`null` Extra=`auto_increment` + * @property {number} [user_id] - NULL=`NO` Key=`MUL` Default=`null` Extra=`` + * @property {number} [delegated_user_id] - NULL=`NO` Key=`MUL` Default=`null` Extra=`` + * @property {string} [permissions] - NULL=`YES` Key=`` Default=`edit` Extra=`` + * @property {number} [permission_level_code] - NULL=`YES` Key=`` Default=`1` Extra=`` + * @property {string} [date_created] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {number} [date_created_code] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {string} [date_created_timestamp] - NULL=`YES` Key=`` Default=`CURRENT_TIMESTAMP` Extra=`DEFAULT_GENERATED` + * @property {string} [date_updated] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {number} [date_updated_code] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {string} [date_updated_timestamp] - NULL=`YES` Key=`` Default=`CURRENT_TIMESTAMP` Extra=`DEFAULT_GENERATED on update CURRENT_TIMESTAMP` + */ + +/** + * @typedef {object} MYSQL_invitations_table_def + * @property {number} [id] - NULL=`NO` Key=`PRI` Default=`null` Extra=`auto_increment` + * @property {number} [inviting_user_id] - NULL=`NO` Key=`MUL` Default=`null` Extra=`` + * @property {string} [invited_user_email] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [invitation_status] - NULL=`YES` Key=`` Default=`Pending` Extra=`` + * @property {string} [database_access] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [priviledge] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [db_tables_data] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [date_created] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {number} [date_created_code] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {string} [date_created_timestamp] - NULL=`YES` Key=`` Default=`CURRENT_TIMESTAMP` Extra=`DEFAULT_GENERATED` + * @property {string} [date_updated] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {number} [date_updated_code] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {string} [date_updated_timestamp] - NULL=`YES` Key=`` Default=`CURRENT_TIMESTAMP` Extra=`DEFAULT_GENERATED on update CURRENT_TIMESTAMP` + */ + +/** + * @typedef {object} MYSQL_docs_pages_table_def + * @property {number} [id] - NULL=`NO` Key=`PRI` Default=`null` Extra=`auto_increment` + * @property {string} [title] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [slug] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [description] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [content] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [text_content] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {number} [level] - NULL=`YES` Key=`` Default=`1` Extra=`` + * @property {number} [page_order] - NULL=`YES` Key=`` Default=`1` Extra=`` + * @property {number} [parent_id] - NULL=`YES` Key=`` Default=`null` Extra=`` + * @property {string} [date_created] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {number} [date_created_code] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {string} [date_created_timestamp] - NULL=`YES` Key=`` Default=`CURRENT_TIMESTAMP` Extra=`DEFAULT_GENERATED` + * @property {string} [date_updated] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {number} [date_updated_code] - NULL=`NO` Key=`` Default=`null` Extra=`` + * @property {string} [date_updated_timestamp] - NULL=`YES` Key=`` Default=`CURRENT_TIMESTAMP` Extra=`DEFAULT_GENERATED on update CURRENT_TIMESTAMP` + */ + +/** + * @typedef {object} MYSQL_delegated_user_tables_table_def + * @property {number} [id] + * @property {number} [delegated_user_id] + * @property {number} [root_user_id] + * @property {string} [database] + * @property {string} [table] + * @property {string} [priviledge] + * @property {string} [date_created] + * @property {number} [date_created_code] + * @property {string} [date_created_timestamp] + * @property {string} [date_updated] + * @property {number} [date_updated_code] + * @property {string} [date_updated_timestamp] + */ + +// React.Dispatch> +// React.MutableRefObject +// React.MutableRefObject>> +// React.LegacyRef + +// /** @type {HTMLFormElement} */ // @ts-ignore +// const formEl = e.target; + +// /** @type {HTMLInputElement} */ // @ts-ignore +// const inputEl = e.target; + +// /** @type {HTMLSelectElement} */ // @ts-ignore +// const selectEl = e.target; + +/** @type {any} */ +/** @type {any} */ // @ts-ignore + +// @ts-ignore + +// @param {object} params + +// /** @type {any} */ +// const dbTablesState = React.useState(0); +// /** @type {[ state: any, dispatch: React.Dispatch> ]} */ // @ts-ignore +// const [dbTables, setDbTables] = dbTablesState; + +// /** @type {import("@/package-shared/types").AddEntryContextType} */ // @ts-ignore +// const init = {}; +// export const AddTableEntryContext = React.createContext(init); diff --git a/package-shared/utils/backend/global-db/DB_HANDLER.js b/package-shared/utils/backend/global-db/DB_HANDLER.js new file mode 100644 index 0000000..b6806bb --- /dev/null +++ b/package-shared/utils/backend/global-db/DB_HANDLER.js @@ -0,0 +1,48 @@ +// @ts-check + +const fs = require("fs"); +const path = require("path"); + +const mysql = require("serverless-mysql"); +const SSL_DIR = "/app/ssl"; + +const MASTER = mysql({ + config: { + host: process.env.DSQL_DB_HOST, + user: process.env.DSQL_DB_USERNAME, + password: process.env.DSQL_DB_PASSWORD, + database: process.env.DSQL_DB_NAME, + port: process.env.DB_PORT ? Number(process.env.DB_PORT) : undefined, + charset: "utf8mb4", + ssl: { + ca: fs.readFileSync(`${SSL_DIR}/ca-cert.pem`), + }, + }, +}); + +/** + * DSQL user read-only DB handler + * @param {object} params + * @param {string} params.paradigm + * @param {string} params.database + * @param {string} params.queryString + * @param {string[]} [params.queryValues] + */ // @ts-ignore +async function DB_HANDLER(...args) { + try { + const results = await MASTER.query(...args); + + /** ********************* Clean up */ + await MASTER.end(); + + return JSON.parse(JSON.stringify(results)); + } catch (/** @type {any} */ error) { + console.log("DB Error =>", error); + return { + success: false, + error: error.message, + }; + } +} + +module.exports = DB_HANDLER; diff --git a/package-shared/utils/backend/global-db/DSQL_USER_DB_HANDLER.js b/package-shared/utils/backend/global-db/DSQL_USER_DB_HANDLER.js new file mode 100644 index 0000000..559fae7 --- /dev/null +++ b/package-shared/utils/backend/global-db/DSQL_USER_DB_HANDLER.js @@ -0,0 +1,129 @@ +// @ts-check + +const fs = require("fs"); +const path = require("path"); + +const mysql = require("serverless-mysql"); + +const SSL_DIR = "/app/ssl"; + +let DSQL_USER = mysql({ + config: { + host: process.env.DSQL_DB_HOST, + user: process.env.DSQL_DB_READ_ONLY_USERNAME, + password: process.env.DSQL_DB_READ_ONLY_PASSWORD, + charset: "utf8mb4", + ssl: { + ca: fs.readFileSync(`${SSL_DIR}/ca-cert.pem`), + }, + }, +}); + +/** + * DSQL user read-only DB handler + * @param {object} params + * @param {"Full Access" | "FA" | "Read Only"} params.paradigm + * @param {string} params.database + * @param {string} params.queryString + * @param {string[]} [params.queryValues] + */ +function DSQL_USER_DB_HANDLER({ + paradigm, + database, + queryString, + queryValues, +}) { + try { + return new Promise((resolve, reject) => { + const fullAccess = paradigm?.match(/full.access|^fa$/i) + ? true + : false; + + try { + if (fullAccess) { + DSQL_USER = mysql({ + config: { + host: process.env.DSQL_DB_HOST, + user: process.env.DSQL_DB_FULL_ACCESS_USERNAME, + password: process.env.DSQL_DB_FULL_ACCESS_PASSWORD, + database: database, + ssl: { + ca: fs.readFileSync(`${SSL_DIR}/ca-cert.pem`), + }, + }, + }); + } else { + DSQL_USER = mysql({ + config: { + host: process.env.DSQL_DB_HOST, + user: process.env.DSQL_DB_READ_ONLY_USERNAME, + password: process.env.DSQL_DB_READ_ONLY_PASSWORD, + database: database, + ssl: { + ca: fs.readFileSync(`${SSL_DIR}/ca-cert.pem`), + }, + }, + }); + } + + /** + * ### Run query Function + * @param {any} results + */ + function runQuery(results) { + DSQL_USER.end(); + resolve(JSON.parse(JSON.stringify(results))); + } + + /** + * ### Query Error + * @param {any} err + */ + function queryError(err) { + DSQL_USER.end(); + resolve({ + error: err.message, + queryStringGenerated: queryString, + queryValuesGenerated: queryValues, + sql: err.sql, + }); + } + + if ( + queryValues && + Array.isArray(queryValues) && + queryValues[0] + ) { + DSQL_USER.query(queryString, queryValues) + .then(runQuery) + .catch(queryError); + } else { + DSQL_USER.query(queryString) + .then(runQuery) + .catch(queryError); + } + + //////////////////////////////////////// + } catch (/** @type {any} */ error) { + //////////////////////////////////////// + + fs.appendFileSync( + "./.tmp/dbErrorLogs.txt", + error.message + "\n" + Date() + "\n\n\n", + "utf8" + ); + + resolve({ + error: error.message, + }); + } + }); + } catch (/** @type {any} */ error) { + return { + success: false, + error: error.message, + }; + } +} + +module.exports = DSQL_USER_DB_HANDLER; diff --git a/package-shared/utils/backend/global-db/NO_DB_HANDLER.js b/package-shared/utils/backend/global-db/NO_DB_HANDLER.js new file mode 100644 index 0000000..1a867a1 --- /dev/null +++ b/package-shared/utils/backend/global-db/NO_DB_HANDLER.js @@ -0,0 +1,63 @@ +// @ts-check + +const fs = require("fs"); +const path = require("path"); + +// const mysql = require("mysql"); + +// const NO_DB = mysql.createConnection({ +// host: process.env.DSQL_DB_HOST, +// user: process.env.DSQL_DB_USERNAME, +// password: process.env.DSQL_DB_PASSWORD, +// charset: "utf8mb4", +// }); + +const mysql = require("serverless-mysql"); + +const SSL_DIR = "/app/ssl"; + +let NO_DB = mysql({ + config: { + host: process.env.DSQL_DB_HOST, + user: process.env.DSQL_DB_USERNAME, + password: process.env.DSQL_DB_PASSWORD, + charset: "utf8mb4", + ssl: { + ca: fs.readFileSync(`${SSL_DIR}/ca-cert.pem`), + }, + }, +}); + +/** + * DSQL user read-only DB handler + * @param {object} params + * @param {string} params.paradigm + * @param {string} params.database + * @param {string} params.queryString + * @param {string[]} [params.queryValues] + */ // @ts-ignore +function NO_DB_HANDLER(...args) { + try { + return new Promise((resolve, reject) => { + NO_DB.query(...args) + .then((results) => { + NO_DB.end(); + resolve(JSON.parse(JSON.stringify(results))); + }) + .catch((err) => { + NO_DB.end(); + resolve({ + error: err.message, + sql: err.sql, + }); + }); + }); + } catch (/** @type {any} */ error) { + return { + success: false, + error: error.message, + }; + } +} + +module.exports = NO_DB_HANDLER; diff --git a/package-shared/utils/backend/global-db/ROOT_DB_HANDLER.js b/package-shared/utils/backend/global-db/ROOT_DB_HANDLER.js new file mode 100644 index 0000000..d18613c --- /dev/null +++ b/package-shared/utils/backend/global-db/ROOT_DB_HANDLER.js @@ -0,0 +1,54 @@ +// @ts-check + +const fs = require("fs"); +const path = require("path"); + +const mysql = require("serverless-mysql"); + +const SSL_DIR = "/app/ssl"; + +let NO_DB = mysql({ + config: { + host: process.env.DSQL_DB_HOST, + user: process.env.DSQL_DB_USERNAME, + password: process.env.DSQL_DB_PASSWORD, + charset: "utf8mb4", + ssl: { + ca: fs.readFileSync(`${SSL_DIR}/ca-cert.pem`), + }, + }, +}); + +/** + * DSQL user read-only DB handler + * @param {object} params + * @param {string} params.paradigm + * @param {string} params.database + * @param {string} params.queryString + * @param {string[]} [params.queryValues] + */ // @ts-ignore +function ROOT_DB_HANDLER(...args) { + try { + return new Promise((resolve, reject) => { + NO_DB.query(...args) + .then((results) => { + NO_DB.end(); + resolve(JSON.parse(JSON.stringify(results))); + }) + .catch((err) => { + NO_DB.end(); + resolve({ + error: err.message, + sql: err.sql, + }); + }); + }); + } catch (/** @type {any} */ error) { + return { + success: false, + error: error.message, + }; + } +} + +module.exports = ROOT_DB_HANDLER; diff --git a/package-shared/utils/backend/global-db/index.js b/package-shared/utils/backend/global-db/index.js new file mode 100644 index 0000000..ae9b9af --- /dev/null +++ b/package-shared/utils/backend/global-db/index.js @@ -0,0 +1,49 @@ +// @ts-check + +const fs = require("fs"); + +const DSQL_USER_DB_HANDLER = require("./DSQL_USER_DB_HANDLER"); + +//////////////////////////////////////// +//////////////////////////////////////// +//////////////////////////////////////// + +process.addListener("exit", async (code) => { + console.log("PROCESS EXITING ..."); +}); + +//////////////////////////////////////// +//////////////////////////////////////// +//////////////////////////////////////// + +/** + * Global function + * ================================================ + * @description this sets all require global variables. This only runs once. + */ +module.exports = function globalFunction() { + /** + * Main Db Handler + */ + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + /** + * Main Db Handler + */ + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + /** + * DSQL user read-only DB handler + * @param {object} params + * @param {string} params.paradigm + * @param {string} params.database + * @param {string} params.queryString + * @param {string[]} [params.queryValues] + */ + DSQL_USER_DB_HANDLER; +}; diff --git a/package-shared/utils/ejson.js b/package-shared/utils/ejson.js new file mode 100644 index 0000000..4630439 --- /dev/null +++ b/package-shared/utils/ejson.js @@ -0,0 +1,38 @@ +/** + * + * @param {string | null | number} string + * @param {(this: any, key: string, value: any) => any} [reviver] + * @returns {{ [key: string]: any } | { [key: string]: any }[] | undefined} + */ +function parse(string, reviver) { + if (!string) return undefined; + if (typeof string == "object") return string; + if (typeof string !== "string") return undefined; + try { + return JSON.parse(string, reviver); + } catch (error) { + return undefined; + } +} + +/** + * + * @param {any} value + * @param {(this: any, key: string, value: any) => any} [replacer] + * @param { string | number } [space] + * @returns {string | undefined} + */ +function stringify(value, replacer, space) { + try { + return JSON.stringify(value, replacer, space); + } catch (error) { + return undefined; + } +} + +const EJSON = { + parse, + stringify, +}; + +module.exports = EJSON; diff --git a/package.json b/package.json index 021cf48..e07883e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "datasquirel", - "version": "2.3.4", + "version": "2.3.5", "description": "Cloud-based SQL data management tool", "main": "index.js", "bin": { @@ -26,11 +26,19 @@ }, "homepage": "https://datasquirel.com/", "dependencies": { + "@types/ace": "^0.0.52", + "@types/react": "^18.3.12", + "@types/tinymce": "^4.6.9", "dotenv": "^16.3.1", + "generate-password": "^1.7.1", + "lodash": "^4.17.21", "mysql": "^2.18.1", - "nodemailer": "^6.9.14" + "nodemailer": "^6.9.14", + "sanitize-html": "^2.13.1", + "serverless-mysql": "^1.5.5" }, "devDependencies": { + "@types/lodash": "^4.17.13", "@types/mysql": "^2.15.21", "@types/node": "^22.7.5" } diff --git a/tsconfig.json b/tsconfig.json index d6d7cc2..377f664 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -29,7 +29,7 @@ "types", "users", "utils", - "package-shared/types" + "package-shared" ], "exclude": ["node_modules", "dump"] } diff --git a/users/update-user.js b/users/update-user.js index fb665ff..173523a 100644 --- a/users/update-user.js +++ b/users/update-user.js @@ -27,7 +27,7 @@ const localUpdateUser = require("../engine/user/update-user"); * @param {object} params - API Key * @param {String} params.key - API Key * @param {String} params.database - Target Database - * @param {{ id: number } & Object.} params.payload - User Object: ID is required + * @param {{ id: number } & Object.} params.payload - User Object: ID is required * * @returns { Promise} */ diff --git a/utils/get.js b/utils/get.js index bfa7b0d..1c97289 100644 --- a/utils/get.js +++ b/utils/get.js @@ -44,19 +44,16 @@ async function get({ key, db, query, queryValues, tableName }) { * @description Look for local db settings in `.env` file and by pass the http request if available */ const { - DSQL_HOST, - DSQL_USER, - DSQL_PASS, + DSQL_DB_HOST, + DSQL_DB_USERNAME, + DSQL_MARIADB_ROOT_PASSWORD, DSQL_DB_NAME, - DSQL_KEY, - DSQL_REF_DB_NAME, - DSQL_FULL_SYNC, } = process.env; if ( - DSQL_HOST?.match(/./) && - DSQL_USER?.match(/./) && - DSQL_PASS?.match(/./) && + DSQL_DB_HOST?.match(/./) && + DSQL_DB_USERNAME?.match(/./) && + DSQL_MARIADB_ROOT_PASSWORD?.match(/./) && DSQL_DB_NAME?.match(/./) ) { /** @type {import("../package-shared/types").DSQL_DatabaseSchemaType | undefined} */