diff --git a/client/auth/loginWithGithub.js b/client/auth/loginWithGithub.js new file mode 100644 index 0000000..4147856 --- /dev/null +++ b/client/auth/loginWithGithub.js @@ -0,0 +1,168 @@ +/** + * @typedef {{ + * fileBase64: string, + * fileBase64Full: string, + * fileName: string, + * fileSize: number, + * fileType: string, + * }} FunctionReturn + */ + +/** + * Login with Google Function + * ============================================================================== + * @description This function takes in a *SINGLE* input file from a HTML file input element. + * HTML file input elements usually return an array of input objects, so be sure to select the target + * file from the array. + * + * @async + * + * @param {object} params - Single object passed + * @param {string} params.clientId - Google app client ID {@link https://datasquirel.com/docs} + * @param {HTMLElement} params.element - HTML Element to display google login button + * @param {boolean} params.triggerPrompt - Whether to trigger Google signing popup or not: {@link https://datasquirel.com/docs} + * @param {function(): void} [params.readyStateDispatch] - React setState Function: sets value to "true" + * + * @returns { Promise } - Return + */ +module.exports = async function loginWithGithub({ clientId, element, triggerPrompt, readyStateDispatch }) { + /** + * == Initialize + * + * @description Initialize + */ + const googleScript = document.createElement("script"); + googleScript.src = "https://accounts.google.com/gsi/client"; + googleScript.className = "social-script-tag"; + + document.body.appendChild(googleScript); + + googleScript.onload = function (e) { + if (google) { + if (readyStateDispatch) readyStateDispatch(true); + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + if (element) { + function handleCredentialResponse(response) { + userLoginWithGoogle({ + gUser: null, + tokenRes: response.credential, + setLoading, + }); + } + + google.accounts.id.initialize({ + client_id: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID, + callback: handleCredentialResponse, + }); + + google.accounts.id.renderButton(document.getElementById("google-identity-button"), { + theme: "outline", + size: "large", + logo_alignment: "center", + }); + } + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + if (triggerPrompt) { + google.accounts.id.prompt( + /** + * Google prompt notification callback + * ======================================================== + * @param {object} notification - Notification object + * @param {function(): string} notification.getMomentType - Notification moment type + * @param {function(): string} notification.getDismissedReason - Notification get Dismissed Reason + * @param {function(): string} notification.getNotDisplayedReason - Notification get Not Displayed Reason + * @param {function(): string} notification.getSkippedReason - Notification get Skipped Reason + * @param {function(): boolean} notification.isDismissedMoment - Notification is Dismissed Moment + * @param {function(): boolean} notification.isDisplayMoment - Notification is Display Moment + * @param {function(): boolean} notification.isDisplayed - Notification is Displayed + * @param {function(): boolean} notification.isNotDisplayed - Notification is Not Displayed + * @param {function(): boolean} notification.isSkippedMoment - Notification is Skipped Moment + */ + (notification) => { + notification.isDisplayed(); + } + ); + } + } + }; + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + const currentLocation = window.location.pathname; + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// +}; + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +/** + * Login with google callback function + * ============================================================================== + * @description This function takes in a *SINGLE* input file from a HTML file input element. + * HTML file input elements usually return an array of input objects, so be sure to select the target + * file from the array. + * + * @async + * + * @param {object} params - Single object passed + * @param {string} params.clientId - Google app client ID {@link https://datasquirel.com/docs} + * @param {HTMLElement} params.element - HTML Element to display google login button + * @param {boolean} params.triggerPrompt - Whether to trigger Google signing popup or not: {@link https://datasquirel.com/docs} + * @param {function(): void} [params.readyStateDispatch] - React setState Function: sets value to "true" + * + * @returns { Promise } - Return + */ +function userLoginWithGoogle({ gUser, tokenRes, setLoading }) { + setLoading(true); + + if (!tokenRes) { + console.log("No Token Response received!"); + return closeLoader(); + } + + fetchApi(`/api/social-login/google-auth${window.location.search}`, { + method: "post", + body: { + token: tokenRes, + }, + }) + .then(async (res) => { + if (res.success && res.user) { + localStorage.setItem("csrf", res.user.csrf_k); + localStorage.setItem("user", JSON.stringify(res.user)); + + window.location.reload(); + } else { + console.log(res); + setLoading(false); + + if (res.alert) { + window.alert(res.msg); + } + } + }) + .catch(async (err) => { + alert("Login Failed"); + + console.log("Google login fetch error => ", err); + + setLoading(false); + }); +} diff --git a/client/auth/loginWithGoogle.js b/client/auth/loginWithGoogle.js new file mode 100644 index 0000000..22b4943 --- /dev/null +++ b/client/auth/loginWithGoogle.js @@ -0,0 +1,183 @@ +/** + * Type Definitions + * =============================================================================== + */ + +/** + * @typedef {object} GoogleIdentityPromptNotification + * @property {function(): string} getMomentType - Notification moment type + * @property {function(): string} getDismissedReason - Notification get Dismissed Reason + * @property {function(): string} getNotDisplayedReason - Notification get Not Displayed Reason + * @property {function(): string} getSkippedReason - Notification get Skipped Reason + * @property {function(): boolean} isDismissedMoment - Notification is Dismissed Moment + * @property {function(): boolean} isDisplayMoment - Notification is Display Moment + * @property {function(): boolean} isDisplayed - Notification is Displayed + * @property {function(): boolean} isNotDisplayed - Notification is Not Displayed + * @property {function(): boolean} isSkippedMoment - Notification is Skipped Moment + */ + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +/** + * Login with Google Function + * =============================================================================== + * @description This function uses google identity api to login a user with datasquirel + * + * @async + * + * @param {object} params - Single object passed + * @param {string} params.username - Username or email + * @param {string} params.database - Target database + * @param {string} params.clientId - Google app client ID: {@link https://datasquirel.com/docs} + * @param {HTMLElement} params.element - HTML Element to display google login button + * @param {boolean} params.triggerPrompt - Whether to trigger Google signing popup or not: {@link https://datasquirel.com/docs} + * @param {function(): void} [params.readyStateDispatch] - React setState Function: sets whether the google login button is ready or not + * + * @returns {Promise} - Return + */ +module.exports = async function loginWithGoogle({ username, database, clientId, element, triggerPrompt, readyStateDispatch }) { + /** + * == Initialize + * + * @description Initialize + */ + const googleScript = document.createElement("script"); + googleScript.src = "https://accounts.google.com/gsi/client"; + googleScript.className = "social-script-tag"; + + document.body.appendChild(googleScript); + + const response = await new Promise((resolve, reject) => { + googleScript.onload = function (e) { + if (google) { + if (readyStateDispatch) readyStateDispatch(true); + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + if (element) { + /** + * Handle google credentials response + * ======================================================== + * @param {object} response - Google response with credentials + * @param {string} response.credential - Google access token + */ + function handleCredentialResponse(response) { + userLoginWithGoogle({ + gUser: null, + accessToken: response.credential, + }).then((result) => { + resolve(result); + }); + } + + google.accounts.id.initialize({ + client_id: clientId, + callback: handleCredentialResponse, + }); + + google.accounts.id.renderButton(document.getElementById("google-identity-button"), { + theme: "outline", + size: "large", + logo_alignment: "center", + }); + } + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + + if (triggerPrompt) { + google.accounts.id.prompt( + /** + * Google prompt notification callback + * ======================================================== + * @param {GoogleIdentityPromptNotification} notification - Notification object + */ + (notification) => { + notification.isDisplayed(); + } + ); + } + } + }; + }); + + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + + /** + * Login with google callback function + * ============================================================================== + * @description This function uses the google token gotten from "loginWithGoogle" function + * and makes the required request to datasquirel + * + * @async + * + * @param {object} params - Single object passed + * @param {string} params.accessToken - Google access token + * + * @returns { Promise } - Return + */ + async function userLoginWithGoogle({ accessToken }) { + if (!accessToken) { + console.log("No Token Response received!"); + return closeLoader(); + } + + return await new Promise((resolve, reject) => { + fetch(`https://datasquirel.com/api/user/google-login`, { + method: "post", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + token: accessToken, + clientId: clientId, + username: username, + database: database, + }), + }) + .then(async (res) => { + if (res.success && res.user) { + localStorage.setItem("csrf", res.user.csrf_k); + localStorage.setItem("user", JSON.stringify(res.user)); + + resolve(true); + } else { + console.log(res); + + if (res.alert) { + window.alert(res.msg); + } + + resolve(false); + } + }) + .catch(async (err) => { + alert("Login Failed"); + console.log("Google login fetch error => ", err); + resolve(false); + }); + }); + } + + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + + return response; +}; diff --git a/client/media/client.js b/client/media/client.js index 671d450..b92d63d 100644 --- a/client/media/client.js +++ b/client/media/client.js @@ -5,6 +5,7 @@ */ import imageInputFileToBase64 from "./imageInputFileToBase64"; import imageInputToBase64 from "./imageInputToBase64"; +import loginWithGoogle from "../auth/loginWithGoogle"; /** ****************************************************************************** */ /** ****************************************************************************** */ @@ -23,6 +24,16 @@ const media = { imageInputFileToBase64: imageInputFileToBase64, }; +/** + * ============================================================================== + * Media Functions Object + * ============================================================================== + */ +const auth = { + imageInputToBase64: imageInputToBase64, + imageInputFileToBase64: imageInputFileToBase64, +}; + /** * ============================================================================== * Main Export diff --git a/client/media/inputFileToBase64.js b/client/media/inputFileToBase64.js index 33a0ad8..3b1dcb4 100644 --- a/client/media/inputFileToBase64.js +++ b/client/media/inputFileToBase64.js @@ -9,32 +9,31 @@ */ /** + * Input File to base64 * ============================================================================== - * Main Function - * ============================================================================== - * @async - * - * @param {{ - * inputFile: { - * name: string, - * size: number, - * type: string, - * }, - * }} params - Single object passed * * @description This function takes in a *SINGLE* input file from a HTML file input element. * HTML file input elements usually return an array of input objects, so be sure to select the target * file from the array. * + * @async + * + * @param {object} params - Single object passed + * @param {object} params.inputFile - HTML input File + * @param {string} params.inputFile.name - Input File Name + * @param {number} params.inputFile.size - Input File Size in bytes + * @param {string} params.inputFile.type - Input File Type: "JPEG", "PNG", "PDF", etc. Whichever allowed regexp is provided + * @param {RegExp} [params.allowedRegex] - Regexp containing the allowed file types + * * @returns { Promise } - Return Object */ -module.exports = async function inputFileToBase64({ inputFile }) { +module.exports = async function inputFileToBase64({ inputFile, allowedRegex }) { /** * == Initialize * * @description Initialize */ - const allowedTypesRegex = /image\/*|\/pdf/; + const allowedTypesRegex = allowedRegex ? allowedRegex : /image\/*|\/pdf/; if (!inputFile?.type?.match(allowedTypesRegex)) { window.alert(`We currently don't support ${inputFile.type} file types. Support is coming soon. For now we support only images and PDFs.`); @@ -49,9 +48,7 @@ module.exports = async function inputFileToBase64({ inputFile }) { } try { - /** - * == Process File - */ + /** Process File **/ let fileName = inputFile.name.replace(/\..*/, ""); /** Add source to new file **/ @@ -66,6 +63,10 @@ module.exports = async function inputFileToBase64({ inputFile }) { }; }); + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// + return { fileBase64: fileData.replace(/.*?base64,/, ""), fileBase64Full: fileData, @@ -73,8 +74,12 @@ module.exports = async function inputFileToBase64({ inputFile }) { fileSize: inputFile.size, fileType: inputFile.type, }; + + //////////////////////////////////////// + //////////////////////////////////////// + //////////////////////////////////////// } catch (error) { - console.log("Image Processing Error! =>", error.message); + console.log("File Processing Error! =>", error.message); return { fileBase64: null, @@ -86,6 +91,6 @@ module.exports = async function inputFileToBase64({ inputFile }) { } }; -/** ********************************************** */ -/** ********************************************** */ -/** ********************************************** */ +//////////////////////////////////////// +//////////////////////////////////////// +//////////////////////////////////////// diff --git a/package.json b/package.json index c906a88..d7cb6ac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "datasquirel", - "version": "1.1.35", + "version": "1.1.36", "description": "Cloud-based SQL data management tool", "main": "index.js", "scripts": {