diff --git a/engine/user/send-email-code.js b/engine/user/send-email-code.js new file mode 100644 index 0000000..9b3f460 --- /dev/null +++ b/engine/user/send-email-code.js @@ -0,0 +1,102 @@ +// @ts-check + +const hashPassword = require("../../functions/hashPassword"); +const varDatabaseDbHandler = require("../engine/utils/varDatabaseDbHandler"); + +/** + * + * @param {object} param0 + * @param {string} param0.email + * @param {import("../../types/database-schema.td").DSQL_DatabaseSchemaType} [param0.dbSchema] + * @param {string} param0.email_login_field + * @returns + */ +async function localSendEmailCode({ email, dbSchema, email_login_field }) { + try { + /** + * User auth + * + * @description Authenticate user + */ + const dbFullName = process.env.DSQL_DB_NAME || ""; + const encryptionKey = process.env.DSQL_ENCRYPTION_KEY || ""; + const encryptionSalt = process.env.DSQL_ENCRYPTION_SALT || ""; + + /** + * Check input validity + * + * @description Check input validity + */ + if (email?.match(/ /)) { + return { + success: false, + msg: "Invalid Email/Password format", + }; + } + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + const tableSchema = dbSchema?.tables.find( + (tb) => tb?.tableName === "users" + ); + + let foundUser = await varDatabaseDbHandler({ + queryString: `SELECT * FROM users WHERE email = ?`, + queryValuesArray: [email], + database: dbFullName.replace(/[^a-z0-9_]/g, ""), + tableSchema, + }); + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + if (!foundUser || !foundUser[0]) + return { + success: false, + payload: null, + msg: "No user found", + }; + + function generateCode() { + const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + let code = ""; + for (let i = 0; i < 8; i++) { + code += chars[Math.floor(Math.random() * chars.length)]; + } + return code; + } + + if (foundUser && foundUser[0] && email_login_field) { + const tempCode = generateCode(); + let setTempCode = await varDatabaseDbHandler({ + queryString: `UPDATE users SET ${email_login_field} = ? WHERE email = ?`, + queryValuesArray: [tempCode, email], + database: dbFullName.replace(/[^a-z0-9_]/g, ""), + tableSchema, + }); + } + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + /** ********************* Send Response */ + return { + success: true, + msg: "Success", + }; + + //////////////////////////////////////// + } catch (/** @type {*} */ error) { + console.log("Error in local login-user Request =>", error.message); + return { + success: false, + msg: "Failed: " + error.message, + }; + } +} + +module.exports = localSendEmailCode; diff --git a/index.js b/index.js index 1f57769..b7d6ff2 100644 --- a/index.js +++ b/index.js @@ -14,6 +14,7 @@ const deleteFile = require("./utils/delete-file"); const createUser = require("./users/add-user"); const updateUser = require("./users/update-user"); const loginUser = require("./users/login-user"); +const sendEmailCode = require("./users/send-email-code"); const logoutUser = require("./users/logout-user"); const userAuth = require("./users/user-auth"); @@ -37,6 +38,7 @@ const sanitizeSql = require("./utils/functions/sanitizeSql"); const user = { createUser: createUser, loginUser: loginUser, + sendEmailCode: sendEmailCode, logoutUser: logoutUser, userAuth: userAuth, reAuthUser: reAuthUser, diff --git a/users/login-user.js b/users/login-user.js index 7a9f98a..a7ee641 100644 --- a/users/login-user.js +++ b/users/login-user.js @@ -191,18 +191,18 @@ async function loginUser({ * * @description https request callback */ - (response) => { + (res) => { var str = ""; - response.on("data", function (chunk) { + res.on("data", function (chunk) { str += chunk; }); - response.on("end", function () { + res.on("end", function () { resolve(JSON.parse(str)); }); - response.on("error", (err) => { + res.on("error", (err) => { reject(err); }); } diff --git a/users/send-email-code.js b/users/send-email-code.js new file mode 100644 index 0000000..cfafc7e --- /dev/null +++ b/users/send-email-code.js @@ -0,0 +1,198 @@ +// @ts-check + +/** + * ============================================================================== + * Imports + * ============================================================================== + */ +const http = require("http"); +const https = require("https"); +const fs = require("fs"); +const path = require("path"); +const encrypt = require("../functions/encrypt"); +const loginLocalUser = require("../engine/user/login-user"); +const localSendEmailCode = require("../engine/user/send-email-code"); + +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ +/** ****************************************************************************** */ + +/** + * @typedef {object} AuthenticatedUser + * @property {boolean} success - Did the function run successfully? + * @property {import("../types/user.td").DATASQUIREL_LoggedInUser | null} payload - Payload of the response + * @property {string} [msg] - An optional message + * @property {number} [userId] - An optional message + */ + +/** + * Send Email Code to a User + * ============================================================================== + * @async + * + * @param {object} params - Single Param object containing params + * @param {String} params.key - FULL ACCESS API Key + * @param {String} params.database - Target Database + * @param {string} params.email Login Email/Username and Password + * @param {http.ServerResponse} params.response - Http response object + * @param {String} params.encryptionKey - Encryption Key + * @param {String} params.encryptionSalt - Encryption Salt + * @param {string} [params.temp_code_field] - Database table field name for temporary code + * + * @returns { Promise} + */ +async function sendEmailCode({ + key, + email, + database, + encryptionKey, + encryptionSalt, + temp_code_field, +}) { + const scheme = process.env.DSQL_HTTP_SCHEME; + const localHost = process.env.DSQL_LOCAL_HOST; + const localHostPort = process.env.DSQL_LOCAL_HOST_PORT; + + const defaultTempLoginFieldName = "temp_login_code"; + const emailLoginTempCodeFieldName = temp_code_field + ? temp_code_field + : defaultTempLoginFieldName; + + /** + * Check Encryption Keys + * + * @description Check Encryption Keys + */ + if (!encryptionKey?.match(/./)) return false; + + if (!encryptionSalt?.match(/./)) return false; + + if (encryptionKey.length < 24) return false; + + if (encryptionSalt.length < 8) return false; + + /** + * Initialize HTTP response variable + */ + let httpResponse; + + /** + * Check for local DB settings + * + * @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_NAME, + DSQL_KEY, + DSQL_REF_DB_NAME, + DSQL_FULL_SYNC, + } = process.env; + + if ( + DSQL_HOST?.match(/./) && + DSQL_USER?.match(/./) && + DSQL_PASS?.match(/./) && + DSQL_DB_NAME?.match(/./) + ) { + /** @type {import("../types/database-schema.td").DSQL_DatabaseSchemaType | undefined} */ + let dbSchema; + + try { + const localDbSchemaPath = path.resolve( + process.cwd(), + "dsql.schema.json" + ); + dbSchema = JSON.parse(fs.readFileSync(localDbSchemaPath, "utf8")); + } catch (error) {} + + if (dbSchema) { + httpResponse = await localSendEmailCode({ + email, + dbSchema, + email_login_field: emailLoginTempCodeFieldName, + }); + } + } else { + /** + * Make https request + * + * @description make a request to datasquirel.com + * + * @type {{ success: boolean, payload: import("../types/user.td").DATASQUIREL_LoggedInUser | null, userId?: number, msg?: string }} + */ + httpResponse = await new Promise((resolve, reject) => { + const reqPayload = JSON.stringify({ + email, + database, + email_login_field: emailLoginTempCodeFieldName, + }); + + const httpsRequest = ( + scheme?.match(/^http$/i) ? http : https + ).request( + { + method: "POST", + headers: { + "Content-Type": "application/json", + "Content-Length": Buffer.from(reqPayload).length, + Authorization: key, + }, + port: localHostPort || 443, + hostname: localHost || "datasquirel.com", + path: `/api/user/send-email-code`, + }, + + /** + * Callback Function + * + * @description https request callback + */ + (res) => { + var str = ""; + + res.on("data", function (chunk) { + str += chunk; + }); + + res.on("end", function () { + resolve(JSON.parse(str)); + }); + + res.on("error", (err) => { + reject(err); + }); + } + ); + + httpsRequest.write(reqPayload); + httpsRequest.end(); + }); + } + + /** ********************************************** */ + /** ********************************************** */ + /** ********************************************** */ + + /** + * Make https request + * + * @description make a request to datasquirel.com + */ + if (httpResponse?.success) { + return true; + } else { + return false; + } +} + +/** ********************************************** */ +/** ********************************************** */ +/** ********************************************** */ + +module.exports = sendEmailCode;