From 3c50291cd3425d5bdc652b7c18c0c0a10f306679 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Sat, 19 Jul 2025 10:11:05 -0400 Subject: [PATCH] Revert "Removed cloud plan cache as it is handled on the license api" --- .../ee/services/license/license-service.ts | 33 ++++++++++++++++++- backend/src/server/routes/index.ts | 1 + .../src/services/project/project-service.ts | 6 +++- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/backend/src/ee/services/license/license-service.ts b/backend/src/ee/services/license/license-service.ts index 1a8d5b2af..7e784d9ad 100644 --- a/backend/src/ee/services/license/license-service.ts +++ b/backend/src/ee/services/license/license-service.ts @@ -8,6 +8,7 @@ import { ForbiddenError } from "@casl/ability"; import { CronJob } from "cron"; import { Knex } from "knex"; +import { TKeyStoreFactory } from "@app/keystore/keystore"; import { getConfig } from "@app/lib/config/env"; import { verifyOfflineLicense } from "@app/lib/crypto"; import { NotFoundError } from "@app/lib/errors"; @@ -46,6 +47,7 @@ type TLicenseServiceFactoryDep = { orgDAL: Pick; permissionService: Pick; licenseDAL: TLicenseDALFactory; + keyStore: Pick; identityOrgMembershipDAL: TIdentityOrgDALFactory; projectDAL: TProjectDALFactory; }; @@ -55,10 +57,14 @@ export type TLicenseServiceFactory = ReturnType; const LICENSE_SERVER_CLOUD_LOGIN = "/api/auth/v1/license-server-login"; const LICENSE_SERVER_ON_PREM_LOGIN = "/api/auth/v1/license-login"; +const LICENSE_SERVER_CLOUD_PLAN_TTL = 5 * 60; // 5 mins +const FEATURE_CACHE_KEY = (orgId: string) => `infisical-cloud-plan-${orgId}`; + export const licenseServiceFactory = ({ orgDAL, permissionService, licenseDAL, + keyStore, identityOrgMembershipDAL, projectDAL }: TLicenseServiceFactoryDep) => { @@ -172,6 +178,12 @@ export const licenseServiceFactory = ({ logger.info(`getPlan: attempting to fetch plan for [orgId=${orgId}] [projectId=${projectId}]`); try { if (instanceType === InstanceType.Cloud) { + const cachedPlan = await keyStore.getItem(FEATURE_CACHE_KEY(orgId)); + if (cachedPlan) { + logger.info(`getPlan: plan fetched from cache [orgId=${orgId}] [projectId=${projectId}]`); + return JSON.parse(cachedPlan) as TFeatureSet; + } + const org = await orgDAL.findOrgById(orgId); if (!org) throw new NotFoundError({ message: `Organization with ID '${orgId}' not found` }); const { @@ -187,12 +199,23 @@ export const licenseServiceFactory = ({ const identityUsed = await licenseDAL.countOrgUsersAndIdentities(orgId); currentPlan.identitiesUsed = identityUsed; + await keyStore.setItemWithExpiry( + FEATURE_CACHE_KEY(org.id), + LICENSE_SERVER_CLOUD_PLAN_TTL, + JSON.stringify(currentPlan) + ); + return currentPlan; } } catch (error) { logger.error( error, - `getPlan: encountered an error when fetching plan [orgId=${orgId}] [projectId=${projectId}] [error]` + `getPlan: encountered an error when fetching pan [orgId=${orgId}] [projectId=${projectId}] [error]` + ); + await keyStore.setItemWithExpiry( + FEATURE_CACHE_KEY(orgId), + LICENSE_SERVER_CLOUD_PLAN_TTL, + JSON.stringify(onPremFeatures) ); return onPremFeatures; } finally { @@ -203,6 +226,7 @@ export const licenseServiceFactory = ({ const refreshPlan = async (orgId: string) => { if (instanceType === InstanceType.Cloud) { + await keyStore.deleteItem(FEATURE_CACHE_KEY(orgId)); await getPlan(orgId); } }; @@ -240,6 +264,7 @@ export const licenseServiceFactory = ({ quantityIdentities }); } + await keyStore.deleteItem(FEATURE_CACHE_KEY(orgId)); } else if (instanceType === InstanceType.EnterpriseOnPrem) { const usedSeats = await licenseDAL.countOfOrgMembers(null, tx); const usedIdentitySeats = await licenseDAL.countOrgUsersAndIdentities(null, tx); @@ -303,6 +328,7 @@ export const licenseServiceFactory = ({ `/api/license-server/v1/customers/${organization.customerId}/session/trial`, { success_url } ); + await keyStore.deleteItem(FEATURE_CACHE_KEY(orgId)); return { url }; }; @@ -679,6 +705,10 @@ export const licenseServiceFactory = ({ return licenses; }; + const invalidateGetPlan = async (orgId: string) => { + await keyStore.deleteItem(FEATURE_CACHE_KEY(orgId)); + }; + return { generateOrgCustomerId, removeOrgCustomer, @@ -693,6 +723,7 @@ export const licenseServiceFactory = ({ return onPremFeatures; }, getPlan, + invalidateGetPlan, updateSubscriptionOrgMemberCount, refreshPlan, getOrgPlan, diff --git a/backend/src/server/routes/index.ts b/backend/src/server/routes/index.ts index b566192f1..4f7ebeed9 100644 --- a/backend/src/server/routes/index.ts +++ b/backend/src/server/routes/index.ts @@ -500,6 +500,7 @@ export const registerRoutes = async ( permissionService, orgDAL, licenseDAL, + keyStore, identityOrgMembershipDAL, projectDAL }); diff --git a/backend/src/services/project/project-service.ts b/backend/src/services/project/project-service.ts index 346352c3e..de35c11f4 100644 --- a/backend/src/services/project/project-service.ts +++ b/backend/src/services/project/project-service.ts @@ -161,8 +161,8 @@ type TProjectServiceFactoryDep = { sshHostGroupDAL: Pick; permissionService: TPermissionServiceFactory; orgService: Pick; + licenseService: Pick; queueService: Pick; - licenseService: Pick; smtpService: Pick; orgDAL: Pick; keyStore: Pick; @@ -489,6 +489,10 @@ export const projectServiceFactory = ({ ); } + // no need to invalidate if there was no limit + if (plan.workspaceLimit) { + await licenseService.invalidateGetPlan(organization.id); + } return { ...project, environments: envs,