diff --git a/packages/backend-core/src/index.ts b/packages/backend-core/src/index.ts index d41f2b9384..f6c091d679 100644 --- a/packages/backend-core/src/index.ts +++ b/packages/backend-core/src/index.ts @@ -37,7 +37,6 @@ export { SearchParams } from "./db" // circular dependencies import * as context from "./context" import * as _tenancy from "./tenancy" -import * as redis from "./redis" export const tenancy = { ..._tenancy, ...context, @@ -52,7 +51,6 @@ export * from "./constants" // expose package init function import * as db from "./db" -export const init = async (opts: any = {}) => { +export const init = (opts: any = {}) => { db.init(opts.db) - await redis.init() } diff --git a/packages/backend-core/src/redis/index.ts b/packages/backend-core/src/redis/index.ts index cc9eb854b4..8d153ea5a1 100644 --- a/packages/backend-core/src/redis/index.ts +++ b/packages/backend-core/src/redis/index.ts @@ -4,7 +4,5 @@ export { default as Client } from "./redis" export * as utils from "./utils" export * as clients from "./init" export * as locks from "./redlockImpl" -export * from "./init" - -export * from "./invite" -export * from "./passwordReset" +export * as invite from "./invite" +export * as passwordReset from "./passwordReset" diff --git a/packages/backend-core/src/redis/init.ts b/packages/backend-core/src/redis/init.ts index 8f2d2914b5..55ffe3dd12 100644 --- a/packages/backend-core/src/redis/init.ts +++ b/packages/backend-core/src/redis/init.ts @@ -7,11 +7,9 @@ let userClient: Client, cacheClient: Client, writethroughClient: Client, lockClient: Client, - socketClient: Client, - inviteClient: Client, - passwordResetClient: Client + socketClient: Client -export async function init() { +async function init() { userClient = await new Client(utils.Databases.USER_CACHE).init() sessionClient = await new Client(utils.Databases.SESSIONS).init() appClient = await new Client(utils.Databases.APP_METADATA).init() @@ -22,8 +20,6 @@ export async function init() { utils.Databases.SOCKET_IO, utils.SelectableDatabase.SOCKET_IO ).init() - inviteClient = await new Client(utils.Databases.INVITATIONS).init() - passwordResetClient = await new Client(utils.Databases.PW_RESETS).init() } export async function shutdown() { @@ -34,8 +30,6 @@ export async function shutdown() { if (writethroughClient) await writethroughClient.finish() if (lockClient) await lockClient.finish() if (socketClient) await socketClient.finish() - if (inviteClient) await inviteClient.finish() - if (passwordResetClient) await passwordResetClient.finish() } process.on("exit", async () => { @@ -90,17 +84,3 @@ export async function getSocketClient() { } return socketClient } - -export async function getInviteClient() { - if (!inviteClient) { - await init() - } - return inviteClient -} - -export async function getPasswordResetClient() { - if (!passwordResetClient) { - await init() - } - return passwordResetClient -} diff --git a/packages/backend-core/src/redis/invite.ts b/packages/backend-core/src/redis/invite.ts index 6e6a1fd9e9..006a06fe26 100644 --- a/packages/backend-core/src/redis/invite.ts +++ b/packages/backend-core/src/redis/invite.ts @@ -1,6 +1,5 @@ -import { utils, tenancy } from "../" +import { utils, tenancy, redis } from "../" import env from "../environment" -import { getInviteClient } from "./init" const TTL_SECONDS = 60 * 60 * 24 * 7 @@ -13,13 +12,25 @@ interface InviteWithCode extends Invite { code: string } +let client: redis.Client + +export async function init() { + if (!client) { + client = new redis.Client(redis.utils.Databases.INVITATIONS) + } + return client +} + +export async function shutdown() { + if (client) await client.finish() +} + /** * Given an invite code and invite body, allow the update an existing/valid invite in redis * @param inviteCode The invite code for an invite in redis * @param value The body of the updated user invitation */ -export async function updateInviteCode(code: string, value: Invite) { - const client = await getInviteClient() +export async function updateCode(code: string, value: Invite) { await client.store(code, value, TTL_SECONDS) } @@ -29,11 +40,7 @@ export async function updateInviteCode(code: string, value: Invite) { * @param info Information to be carried along with the invitation. * @return returns the code that was stored to redis. */ -export async function createInviteCode( - email: string, - info: any -): Promise { - const client = await getInviteClient() +export async function createCode(email: string, info: any): Promise { const code = utils.newid() await client.store(code, { email, info }, TTL_SECONDS) return code @@ -44,8 +51,7 @@ export async function createInviteCode( * @param inviteCode the invite code that was provided as part of the link. * @return If the code is valid then an email address will be returned. */ -export async function getInviteCode(code: string): Promise { - const client = await getInviteClient() +export async function getCode(code: string): Promise { const value = (await client.get(code)) as Invite | undefined if (!value) { throw "Invitation is not valid or has expired, please request a new one." @@ -53,8 +59,7 @@ export async function getInviteCode(code: string): Promise { return value } -export async function deleteInviteCode(code: string) { - const client = await getInviteClient() +export async function deleteCode(code: string) { await client.delete(code) } @@ -62,7 +67,6 @@ export async function deleteInviteCode(code: string) { Get all currently available user invitations for the current tenant. **/ export async function getInviteCodes(): Promise { - const client = await getInviteClient() const invites: { key: string; value: Invite }[] = await client.scan() const results: InviteWithCode[] = invites.map(invite => { diff --git a/packages/backend-core/src/redis/passwordReset.ts b/packages/backend-core/src/redis/passwordReset.ts index 243b73c529..13c1b1d2e6 100644 --- a/packages/backend-core/src/redis/passwordReset.ts +++ b/packages/backend-core/src/redis/passwordReset.ts @@ -1,5 +1,4 @@ -import { utils } from "../" -import { getPasswordResetClient } from "./init" +import { redis, utils } from "../" const TTL_SECONDS = 60 * 60 @@ -8,6 +7,19 @@ interface PasswordReset { info: any } +let client: redis.Client + +export async function init() { + if (!client) { + client = new redis.Client(redis.utils.Databases.PW_RESETS) + } + return client +} + +export async function shutdown() { + if (client) await client.finish() +} + /** * 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). @@ -15,11 +27,7 @@ interface PasswordReset { * @param info Info about the user/the reset process. * @return returns the code that was stored to redis. */ -export async function createResetPasswordCode( - userId: string, - info: any -): Promise { - const client = await getPasswordResetClient() +export async function createCode(userId: string, info: any): Promise { const code = utils.newid() await client.store(code, { userId, info }, TTL_SECONDS) return code @@ -30,10 +38,7 @@ export async function createResetPasswordCode( * @param code The code provided via the email link. * @return returns the user ID if it is found */ -export async function getResetPasswordCode( - code: string -): Promise { - const client = await getPasswordResetClient() +export async function getCode(code: string): Promise { const value = (await client.get(code)) as PasswordReset | undefined if (!value) { throw "Provided information is not valid, cannot reset password - please try again." diff --git a/packages/worker/src/api/controllers/global/users.ts b/packages/worker/src/api/controllers/global/users.ts index cd11fc74b6..affdaa9938 100644 --- a/packages/worker/src/api/controllers/global/users.ts +++ b/packages/worker/src/api/controllers/global/users.ts @@ -308,7 +308,7 @@ export const checkInvite = async (ctx: any) => { const { code } = ctx.params let invite try { - invite = await redis.getInviteCode(code) + invite = await redis.invite.getCode(code) } catch (e) { console.warn("Error getting invite from code", e) ctx.throw(400, "There was a problem with the invite") @@ -322,7 +322,7 @@ export const checkInvite = async (ctx: any) => { export const getUserInvites = async (ctx: any) => { try { // Restricted to the currently authenticated tenant - ctx.body = await redis.getInviteCodes() + ctx.body = await redis.invite.getInviteCodes() } catch (e) { ctx.throw(400, "There was a problem fetching invites") } @@ -336,7 +336,7 @@ export const updateInvite = async (ctx: any) => { let invite try { - invite = await redis.getInviteCode(code) + invite = await redis.invite.getCode(code) } catch (e) { ctx.throw(400, "There was a problem with the invite") return @@ -364,7 +364,7 @@ export const updateInvite = async (ctx: any) => { } } - await redis.updateInviteCode(code, updated) + await redis.invite.updateCode(code, updated) ctx.body = { ...invite } } @@ -374,8 +374,8 @@ export const inviteAccept = async ( const { inviteCode, password, firstName, lastName } = ctx.request.body try { // info is an extension of the user object that was stored by global - const { email, info }: any = await redis.getInviteCode(inviteCode) - await redis.deleteInviteCode(inviteCode) + const { email, info }: any = await redis.invite.getCode(inviteCode) + await redis.invite.deleteCode(inviteCode) const user = await tenancy.doInTenant(info.tenantId, async () => { let request: any = { firstName, diff --git a/packages/worker/src/db/index.ts b/packages/worker/src/db/index.ts index 19f8f8acee..157c2f4fb3 100644 --- a/packages/worker/src/db/index.ts +++ b/packages/worker/src/db/index.ts @@ -1,7 +1,7 @@ import * as core from "@budibase/backend-core" import env from "../environment" -export async function init() { +export function init() { const dbConfig: any = { replication: true, find: true, @@ -12,5 +12,5 @@ export async function init() { dbConfig.allDbs = true } - await core.init({ db: dbConfig }) + core.init({ db: dbConfig }) } diff --git a/packages/worker/src/index.ts b/packages/worker/src/index.ts index d40c7f0668..e486a67433 100644 --- a/packages/worker/src/index.ts +++ b/packages/worker/src/index.ts @@ -16,8 +16,9 @@ import { queue, env as coreEnv, timers, + redis, } from "@budibase/backend-core" - +db.init() import Koa from "koa" import koaBody from "koa-body" import http from "http" @@ -71,6 +72,8 @@ server.on("close", async () => { shuttingDown = true console.log("Server Closed") timers.cleanup() + await redis.invite.shutdown() + await redis.passwordReset.shutdown() await events.shutdown() await queue.shutdown() if (!env.isTest()) { @@ -85,8 +88,9 @@ const shutdown = () => { export default server.listen(parseInt(env.PORT || "4002"), async () => { console.log(`Worker running on ${JSON.stringify(server.address())}`) - await db.init() await initPro() + await redis.invite.init() + await redis.passwordReset.init() // configure events to use the pro audit log write // can't integrate directly into backend-core due to cyclic issues await events.processors.init(proSdk.auditLogs.write) diff --git a/packages/worker/src/sdk/auth/auth.ts b/packages/worker/src/sdk/auth/auth.ts index 2140b89ce3..704de9e4b2 100644 --- a/packages/worker/src/sdk/auth/auth.ts +++ b/packages/worker/src/sdk/auth/auth.ts @@ -73,7 +73,7 @@ export const reset = async (email: string) => { * Perform the user password update if the provided reset code is valid. */ export const resetUpdate = async (resetCode: string, password: string) => { - const { userId } = await redis.getResetPasswordCode(resetCode) + const { userId } = await redis.passwordReset.getCode(resetCode) let user = await userSdk.db.getUser(userId) user.password = password diff --git a/packages/worker/src/tests/TestConfiguration.ts b/packages/worker/src/tests/TestConfiguration.ts index 1961d22c34..d4fcbeebd6 100644 --- a/packages/worker/src/tests/TestConfiguration.ts +++ b/packages/worker/src/tests/TestConfiguration.ts @@ -7,6 +7,7 @@ mocks.licenses.init(mocks.pro) mocks.licenses.useUnlimited() import * as dbConfig from "../db" +dbConfig.init() import env from "../environment" import * as controllers from "./controllers" const supertest = require("supertest") @@ -108,7 +109,6 @@ class TestConfiguration { // SETUP / TEARDOWN async beforeAll() { - await dbConfig.init() try { await this.createDefaultUser() await this.createSession(this.user!) diff --git a/packages/worker/src/utilities/email.ts b/packages/worker/src/utilities/email.ts index a4d2c296e5..530b6ce87f 100644 --- a/packages/worker/src/utilities/email.ts +++ b/packages/worker/src/utilities/email.ts @@ -3,7 +3,6 @@ import { EmailTemplatePurpose, TemplateType } from "../constants" import { getTemplateByPurpose, EmailTemplates } from "../constants/templates" import { getSettingsTemplateContext } from "./templates" import { processString } from "@budibase/string-templates" -import { createResetPasswordCode } from "@budibase/backend-core/src/redis/passwordReset" import { redis } from "@budibase/backend-core" import { User, SendEmailOpts, SMTPInnerConfig } from "@budibase/types" import { configs } from "@budibase/backend-core" @@ -62,9 +61,9 @@ async function getLinkCode( ) { switch (purpose) { case EmailTemplatePurpose.PASSWORD_RECOVERY: - return createResetPasswordCode(user._id!, info) + return redis.passwordReset.createCode(user._id!, info) case EmailTemplatePurpose.INVITATION: - return redis.createInviteCode(email, info) + return redis.invite.createCode(email, info) default: return null }