const { Client, utils } = require("@budibase/backend-core/redis") const { newid } = require("@budibase/backend-core/utils") function getExpirySecondsForDB(db) { switch (db) { case utils.Databases.PW_RESETS: // a hour return 3600 case utils.Databases.INVITATIONS: // a day return 86400 } } let pwResetClient, invitationClient function getClient(db) { switch (db) { case utils.Databases.PW_RESETS: return pwResetClient case utils.Databases.INVITATIONS: return invitationClient } } async function writeACode(db, value) { const client = await getClient(db) const code = newid() await client.store(code, value, getExpirySecondsForDB(db)) return code } async function getACode(db, code, deleteCode = true) { const client = await getClient(db) const value = await client.get(code) if (!value) { throw "Invalid code." } if (deleteCode) { await client.delete(code) } return value } exports.init = async () => { pwResetClient = new Client(utils.Databases.PW_RESETS) invitationClient = new Client(utils.Databases.INVITATIONS) await pwResetClient.init() await invitationClient.init() } /** * make sure redis connection is closed. */ exports.shutdown = async () => { if (pwResetClient) await pwResetClient.finish() if (invitationClient) await invitationClient.finish() console.log("Redis shutdown") } /** * Given a user ID this will store a code (that is returned) for an hour in redis. * The user can then return this code for resetting their password (through their reset link). * @param {string} userId the ID of the user which is to be reset. * @param {object} info Info about the user/the reset process. * @return {Promise} returns the code that was stored to redis. */ exports.getResetPasswordCode = async (userId, info) => { return writeACode(utils.Databases.PW_RESETS, { userId, info }) } /** * Given a reset code this will lookup to redis, check if the code is valid and delete if required. * @param {string} resetCode The code provided via the email link. * @param {boolean} deleteCode If the code is used/finished with this will delete it - defaults to true. * @return {Promise} returns the user ID if it is found */ exports.checkResetPasswordCode = async (resetCode, deleteCode = true) => { try { return getACode(utils.Databases.PW_RESETS, resetCode, deleteCode) } catch (err) { throw "Provided information is not valid, cannot reset password - please try again." } } /** * Generates an invitation code and writes it to redis - which can later be checked for user creation. * @param {string} email the email address which the code is being sent to (for use later). * @param {object|null} info Information to be carried along with the invitation. * @return {Promise} returns the code that was stored to redis. */ exports.getInviteCode = async (email, info) => { return writeACode(utils.Databases.INVITATIONS, { email, info }) } /** * Checks that the provided invite code is valid - will return the email address of user that was invited. * @param {string} inviteCode the invite code that was provided as part of the link. * @param {boolean} deleteCode whether or not the code should be deleted after retrieval - defaults to true. * @return {Promise} If the code is valid then an email address will be returned. */ exports.checkInviteCode = async (inviteCode, deleteCode = true) => { try { return getACode(utils.Databases.INVITATIONS, inviteCode, deleteCode) } catch (err) { throw "Invitation is not valid or has expired, please request a new one." } }