feat(server): added keystore and made server cfg fetch from keystore to avoid db calls

This commit is contained in:
Akhil Mohan
2024-02-26 19:52:38 +05:30
parent 1cccbca0c5
commit 7d8b399102
9 changed files with 77 additions and 18 deletions
+19
View File
@@ -0,0 +1,19 @@
import { TKeyStoreFactory } from "@app/keystore/keystore";
export const mockKeyStore = (): TKeyStoreFactory => {
const store: Record<string, string | number | Buffer> = {};
return {
setItem: async (key, value) => {
store[key] = value;
return "OK";
},
getItem: async (key) => {
const value = store[key];
if (typeof value === "string") {
return value;
}
return null;
}
};
};
+3 -1
View File
@@ -14,6 +14,7 @@ import { AuthTokenType } from "@app/services/auth/auth-type";
import { mockQueue } from "./mocks/queue"; import { mockQueue } from "./mocks/queue";
import { mockSmtpServer } from "./mocks/smtp"; import { mockSmtpServer } from "./mocks/smtp";
import { mockKeyStore } from "./mocks/keystore";
dotenv.config({ path: path.join(__dirname, "../../.env.test"), debug: true }); dotenv.config({ path: path.join(__dirname, "../../.env.test"), debug: true });
export default { export default {
@@ -41,7 +42,8 @@ export default {
await db.seed.run(); await db.seed.run();
const smtp = mockSmtpServer(); const smtp = mockSmtpServer();
const queue = mockQueue(); const queue = mockQueue();
const server = await main({ db, smtp, logger, queue }); const keyStore = mockKeyStore();
const server = await main({ db, smtp, logger, queue, keyStore });
// @ts-expect-error type // @ts-expect-error type
globalThis.testServer = server; globalThis.testServer = server;
// @ts-expect-error type // @ts-expect-error type
-6
View File
@@ -1,6 +0,0 @@
import Redis from "ioredis";
export const initRedisConnection = (redisUrl: string) => {
const redis = new Redis(redisUrl);
return redis;
};
+13
View File
@@ -0,0 +1,13 @@
import { Redis } from "ioredis";
export type TKeyStoreFactory = ReturnType<typeof keyStoreFactory>;
export const keyStoreFactory = (redisUrl: string) => {
const redis = new Redis(redisUrl);
const setItem = async (key: string, value: string | number | Buffer) => redis.set(key, value);
const getItem = async (key: string) => redis.get(key);
return { setItem, getItem };
};
+3 -1
View File
@@ -1,6 +1,7 @@
import dotenv from "dotenv"; import dotenv from "dotenv";
import { initDbConnection } from "./db"; import { initDbConnection } from "./db";
import { keyStoreFactory } from "./keystore/keystore";
import { formatSmtpConfig, initEnvConfig } from "./lib/config/env"; import { formatSmtpConfig, initEnvConfig } from "./lib/config/env";
import { initLogger } from "./lib/logger"; import { initLogger } from "./lib/logger";
import { queueServiceFactory } from "./queue"; import { queueServiceFactory } from "./queue";
@@ -19,8 +20,9 @@ const run = async () => {
const smtp = smtpServiceFactory(formatSmtpConfig()); const smtp = smtpServiceFactory(formatSmtpConfig());
const queue = queueServiceFactory(appCfg.REDIS_URL); const queue = queueServiceFactory(appCfg.REDIS_URL);
const keyStore = keyStoreFactory(appCfg.REDIS_URL);
const server = await main({ db, smtp, logger, queue }); const server = await main({ db, smtp, logger, queue, keyStore });
const bootstrap = await bootstrapCheck({ db }); const bootstrap = await bootstrapCheck({ db });
// eslint-disable-next-line // eslint-disable-next-line
process.on("SIGINT", async () => { process.on("SIGINT", async () => {
+4 -2
View File
@@ -14,6 +14,7 @@ import fasitfy from "fastify";
import { Knex } from "knex"; import { Knex } from "knex";
import { Logger } from "pino"; import { Logger } from "pino";
import { TKeyStoreFactory } from "@app/keystore/keystore";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { TQueueServiceFactory } from "@app/queue"; import { TQueueServiceFactory } from "@app/queue";
import { TSmtpService } from "@app/services/smtp/smtp-service"; import { TSmtpService } from "@app/services/smtp/smtp-service";
@@ -31,10 +32,11 @@ type TMain = {
smtp: TSmtpService; smtp: TSmtpService;
logger?: Logger; logger?: Logger;
queue: TQueueServiceFactory; queue: TQueueServiceFactory;
keyStore: TKeyStoreFactory;
}; };
// Run the server! // Run the server!
export const main = async ({ db, smtp, logger, queue }: TMain) => { export const main = async ({ db, smtp, logger, queue, keyStore }: TMain) => {
const appCfg = getConfig(); const appCfg = getConfig();
const server = fasitfy({ const server = fasitfy({
logger: appCfg.NODE_ENV === "test" ? false : logger, logger: appCfg.NODE_ENV === "test" ? false : logger,
@@ -70,7 +72,7 @@ export const main = async ({ db, smtp, logger, queue }: TMain) => {
} }
await server.register(helmet, { contentSecurityPolicy: false }); await server.register(helmet, { contentSecurityPolicy: false });
await server.register(registerRoutes, { smtp, queue, db }); await server.register(registerRoutes, { smtp, queue, db, keyStore });
if (appCfg.isProductionMode) { if (appCfg.isProductionMode) {
await server.register(registerExternalNextjs, { await server.register(registerExternalNextjs, {
+9 -2
View File
@@ -34,6 +34,7 @@ import { snapshotFolderDALFactory } from "@app/ee/services/secret-snapshot/snaps
import { snapshotSecretDALFactory } from "@app/ee/services/secret-snapshot/snapshot-secret-dal"; import { snapshotSecretDALFactory } from "@app/ee/services/secret-snapshot/snapshot-secret-dal";
import { trustedIpDALFactory } from "@app/ee/services/trusted-ip/trusted-ip-dal"; import { trustedIpDALFactory } from "@app/ee/services/trusted-ip/trusted-ip-dal";
import { trustedIpServiceFactory } from "@app/ee/services/trusted-ip/trusted-ip-service"; import { trustedIpServiceFactory } from "@app/ee/services/trusted-ip/trusted-ip-service";
import { TKeyStoreFactory } from "@app/keystore/keystore";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { TQueueServiceFactory } from "@app/queue"; import { TQueueServiceFactory } from "@app/queue";
import { apiKeyDALFactory } from "@app/services/api-key/api-key-dal"; import { apiKeyDALFactory } from "@app/services/api-key/api-key-dal";
@@ -112,7 +113,12 @@ import { registerV3Routes } from "./v3";
export const registerRoutes = async ( export const registerRoutes = async (
server: FastifyZodProvider, server: FastifyZodProvider,
{ db, smtp: smtpService, queue: queueService }: { db: Knex; smtp: TSmtpService; queue: TQueueServiceFactory } {
db,
smtp: smtpService,
queue: queueService,
keyStore
}: { db: Knex; smtp: TSmtpService; queue: TQueueServiceFactory; keyStore: TKeyStoreFactory }
) => { ) => {
await server.register(registerSecretScannerGhApp, { prefix: "/ss-webhook" }); await server.register(registerSecretScannerGhApp, { prefix: "/ss-webhook" });
@@ -263,7 +269,8 @@ export const registerRoutes = async (
userDAL, userDAL,
authService: loginService, authService: loginService,
serverCfgDAL: superAdminDAL, serverCfgDAL: superAdminDAL,
orgService orgService,
keyStore
}); });
const apiKeyService = apiKeyServiceFactory({ apiKeyDAL, userDAL }); const apiKeyService = apiKeyServiceFactory({ apiKeyDAL, userDAL });
+1 -1
View File
@@ -16,7 +16,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
schema: { schema: {
response: { response: {
200: z.object({ 200: z.object({
config: SuperAdminSchema config: SuperAdminSchema.omit({ createdAt: true, updatedAt: true })
}) })
} }
}, },
@@ -1,4 +1,5 @@
import { TSuperAdmin, TSuperAdminUpdate } from "@app/db/schemas"; import { TSuperAdmin, TSuperAdminUpdate } from "@app/db/schemas";
import { TKeyStoreFactory } from "@app/keystore/keystore";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
@@ -14,6 +15,7 @@ type TSuperAdminServiceFactoryDep = {
userDAL: TUserDALFactory; userDAL: TUserDALFactory;
authService: Pick<TAuthLoginFactory, "generateUserTokens">; authService: Pick<TAuthLoginFactory, "generateUserTokens">;
orgService: Pick<TOrgServiceFactory, "createOrganization">; orgService: Pick<TOrgServiceFactory, "createOrganization">;
keyStore: Pick<TKeyStoreFactory, "getItem" | "setItem">;
}; };
export type TSuperAdminServiceFactory = ReturnType<typeof superAdminServiceFactory>; export type TSuperAdminServiceFactory = ReturnType<typeof superAdminServiceFactory>;
@@ -21,26 +23,44 @@ export type TSuperAdminServiceFactory = ReturnType<typeof superAdminServiceFacto
// eslint-disable-next-line // eslint-disable-next-line
export let getServerCfg: () => Promise<TSuperAdmin>; export let getServerCfg: () => Promise<TSuperAdmin>;
const ADMIN_CONFIG_KEY = "infisical-admin-cfg";
export const superAdminServiceFactory = ({ export const superAdminServiceFactory = ({
serverCfgDAL, serverCfgDAL,
userDAL, userDAL,
authService, authService,
orgService orgService,
keyStore
}: TSuperAdminServiceFactoryDep) => { }: TSuperAdminServiceFactoryDep) => {
const initServerCfg = async () => { const initServerCfg = async () => {
// TODO(akhilmhdh): bad pattern time less change this later to me itself // TODO(akhilmhdh): bad pattern time less change this later to me itself
getServerCfg = () => serverCfgDAL.findOne({}); getServerCfg = async () => {
const config = await keyStore.getItem(ADMIN_CONFIG_KEY);
if (!config) {
const serverCfg = await serverCfgDAL.findOne({});
await keyStore.setItem(ADMIN_CONFIG_KEY, JSON.stringify(serverCfg));
return serverCfg;
}
const keyStoreServerCfg = JSON.parse(config) as TSuperAdmin;
return {
...keyStoreServerCfg,
// this is to allow admin router to work
createdAt: new Date(keyStoreServerCfg.createdAt),
updatedAt: new Date(keyStoreServerCfg.updatedAt)
};
};
const serverCfg = await serverCfgDAL.findOne({}); const serverCfg = await getServerCfg();
if (serverCfg) return; if (serverCfg) return;
const newCfg = await serverCfgDAL.create({ initialized: false, allowSignUp: true }); const newCfg = await serverCfgDAL.create({ initialized: false, allowSignUp: true });
return newCfg; return newCfg;
}; };
const updateServerCfg = async (data: TSuperAdminUpdate) => { const updateServerCfg = async (data: TSuperAdminUpdate) => {
const serverCfg = await getServerCfg(); const serverCfg = await getServerCfg();
const cfg = await serverCfgDAL.updateById(serverCfg.id, data); const updatedServerCfg = await serverCfgDAL.updateById(serverCfg.id, data);
return cfg; await keyStore.setItem(ADMIN_CONFIG_KEY, JSON.stringify(updatedServerCfg));
return updatedServerCfg;
}; };
const adminSignUp = async ({ const adminSignUp = async ({