feat: adding upstash cache
This commit is contained in:
@@ -46,6 +46,7 @@
|
||||
"@react-email/components": "^0.0.42",
|
||||
"@sentry/node": "^9.30.0",
|
||||
"@supabase/supabase-js": "^2.46.2",
|
||||
"@upstash/redis": "^1.35.1",
|
||||
"ai": "^4.3.10",
|
||||
"autumn-js": "^0.0.77",
|
||||
"axios": "^1.8.3",
|
||||
|
||||
@@ -25,12 +25,15 @@ import { internalFeatureRouter } from "../features/internalFeatureRouter.js";
|
||||
import { analyticsRouter } from "../analytics/analyticsRouter.js";
|
||||
import { handleConnectStripe } from "../orgs/handlers/handleConnectStripe.js";
|
||||
import { handleDeleteStripe } from "../orgs/handlers/handleDeleteStripe.js";
|
||||
import { refreshCusCache } from "../customers/cusCache/updateCachedCus.js";
|
||||
import { refreshCacheMiddleware } from "@/middleware/refreshCacheMiddleware.js";
|
||||
|
||||
const apiRouter: Router = Router();
|
||||
|
||||
apiRouter.use(apiAuthMiddleware);
|
||||
apiRouter.use(pricingMiddleware);
|
||||
apiRouter.use(analyticsMiddleware);
|
||||
apiRouter.use(refreshCacheMiddleware);
|
||||
|
||||
apiRouter.use("/customers", cusRouter);
|
||||
apiRouter.use("/invoices", invoiceRouter);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { getCusWithCache } from "@/internal/customers/cusCache/getCusWithCache.js";
|
||||
import {
|
||||
ACTIVE_STATUSES,
|
||||
RELEVANT_STATUSES,
|
||||
@@ -46,25 +47,32 @@ export const getEntityResponse = async ({
|
||||
apiVersion: number;
|
||||
features: Feature[];
|
||||
}) => {
|
||||
let customer = await CusService.getFull({
|
||||
db,
|
||||
// let customer = await CusService.getFull({
|
||||
// db,
|
||||
// idOrInternalId: customerId,
|
||||
// orgId: org.id,
|
||||
// env,
|
||||
// withEntities: true,
|
||||
// withSubs: true,
|
||||
// expand,
|
||||
// entityId,
|
||||
// });
|
||||
let customer = await getCusWithCache({
|
||||
idOrInternalId: customerId,
|
||||
orgId: org.id,
|
||||
env,
|
||||
withEntities: true,
|
||||
withSubs: true,
|
||||
expand,
|
||||
entityId,
|
||||
});
|
||||
|
||||
let entities = customer.entities.filter((e: Entity) =>
|
||||
entityIds.includes(e.id),
|
||||
entityIds.includes(e.id)
|
||||
);
|
||||
|
||||
let entityCusProducts = customer.customer_products.filter(
|
||||
(p: FullCusProduct) =>
|
||||
entities.some((e: Entity) => e.internal_id == p.internal_entity_id) ||
|
||||
nullish(p.internal_entity_id),
|
||||
nullish(p.internal_entity_id)
|
||||
);
|
||||
|
||||
let subs = customer.subscriptions || [];
|
||||
@@ -72,7 +80,7 @@ export const getEntityResponse = async ({
|
||||
const entityResponses: EntityResponse[] = [];
|
||||
for (const entityId of entityIds) {
|
||||
let entity = customer.entities.find(
|
||||
(e: Entity) => e.id == entityId || e.internal_id == entityId,
|
||||
(e: Entity) => e.id == entityId || e.internal_id == entityId
|
||||
);
|
||||
if (!entity) {
|
||||
throw new RecaseError({
|
||||
@@ -84,8 +92,8 @@ export const getEntityResponse = async ({
|
||||
|
||||
let entitySubs = subs.filter((s: Subscription) =>
|
||||
entityCusProducts.some((p: FullCusProduct) =>
|
||||
p.subscription_ids?.includes(s.stripe_id || ""),
|
||||
),
|
||||
p.subscription_ids?.includes(s.stripe_id || "")
|
||||
)
|
||||
);
|
||||
|
||||
let { main, addOns } = await processFullCusProducts({
|
||||
|
||||
@@ -25,44 +25,6 @@ import { getV2CheckResponse } from "./checkUtils/getV2CheckResponse.js";
|
||||
|
||||
export const checkRouter: Router = Router();
|
||||
|
||||
const getRequiredAndActualBalance = ({
|
||||
cusEnts,
|
||||
feature,
|
||||
originalFeatureId,
|
||||
required,
|
||||
entityId,
|
||||
}: {
|
||||
cusEnts: FullCustomerEntitlement[];
|
||||
feature: Feature;
|
||||
originalFeatureId: string;
|
||||
required: number;
|
||||
entityId: string;
|
||||
}) => {
|
||||
let requiredBalance = required;
|
||||
if (
|
||||
feature.type === FeatureType.CreditSystem &&
|
||||
feature.id !== originalFeatureId
|
||||
) {
|
||||
requiredBalance = featureToCreditSystem({
|
||||
featureId: originalFeatureId,
|
||||
creditSystem: feature,
|
||||
amount: required,
|
||||
});
|
||||
}
|
||||
|
||||
const actualBalance = getFeatureBalance({
|
||||
cusEnts,
|
||||
internalFeatureId: feature.internal_id!,
|
||||
entityId,
|
||||
});
|
||||
|
||||
return {
|
||||
required: requiredBalance,
|
||||
actual: actualBalance,
|
||||
entityId,
|
||||
};
|
||||
};
|
||||
|
||||
checkRouter.post("", async (req: any, res: any) => {
|
||||
try {
|
||||
let {
|
||||
@@ -181,7 +143,7 @@ checkRouter.post("", async (req: any, res: any) => {
|
||||
|
||||
const { allowed, balance } = v2Response;
|
||||
const featureToUse = allFeatures.find(
|
||||
(f: Feature) => f.id === v2Response.feature_id,
|
||||
(f: Feature) => f.id === v2Response.feature_id
|
||||
);
|
||||
|
||||
if (allowed && req.isPublic !== true) {
|
||||
|
||||
@@ -17,6 +17,7 @@ import { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { getFullCusQuery } from "./getFullCusQuery.js";
|
||||
import { trace } from "@opentelemetry/api";
|
||||
import { withSpan } from "../analytics/tracer/spanUtils.js";
|
||||
import { RELEVANT_STATUSES } from "./cusProducts/CusProductService.js";
|
||||
|
||||
const tracer = trace.getTracer("express");
|
||||
|
||||
@@ -26,11 +27,7 @@ export class CusService {
|
||||
idOrInternalId,
|
||||
orgId,
|
||||
env,
|
||||
inStatuses = [
|
||||
CusProductStatus.Active,
|
||||
CusProductStatus.PastDue,
|
||||
CusProductStatus.Scheduled,
|
||||
],
|
||||
inStatuses = RELEVANT_STATUSES,
|
||||
withEntities = false,
|
||||
entityId,
|
||||
expand,
|
||||
@@ -72,7 +69,7 @@ export class CusService {
|
||||
withEntities,
|
||||
withTrialsUsed,
|
||||
withSubs,
|
||||
entityId,
|
||||
entityId
|
||||
);
|
||||
|
||||
let result = await db.execute(query);
|
||||
@@ -123,10 +120,10 @@ export class CusService {
|
||||
where: and(
|
||||
or(
|
||||
eq(customers.id, idOrInternalId),
|
||||
eq(customers.internal_id, idOrInternalId),
|
||||
eq(customers.internal_id, idOrInternalId)
|
||||
),
|
||||
eq(customers.org_id, orgId),
|
||||
eq(customers.env, env),
|
||||
eq(customers.env, env)
|
||||
),
|
||||
});
|
||||
|
||||
@@ -152,7 +149,7 @@ export class CusService {
|
||||
where: and(
|
||||
eq(customers.email, email),
|
||||
eq(customers.org_id, orgId),
|
||||
eq(customers.env, env),
|
||||
eq(customers.env, env)
|
||||
),
|
||||
});
|
||||
|
||||
@@ -270,8 +267,8 @@ export class CusService {
|
||||
and(
|
||||
eq(customers.internal_id, internalId),
|
||||
eq(customers.org_id, orgId),
|
||||
eq(customers.env, env),
|
||||
),
|
||||
eq(customers.env, env)
|
||||
)
|
||||
)
|
||||
.returning();
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import { handleAttachErrors } from "./attachUtils/handleAttachErrors.js";
|
||||
import { checkStripeConnections, createStripePrices } from "./attachRouter.js";
|
||||
import { insertCustomItems } from "./attachUtils/insertCustomItems.js";
|
||||
import { runAttachFunction } from "./attachUtils/getAttachFunction.js";
|
||||
import { refreshCusCache } from "../cusCache/updateCachedCus.js";
|
||||
|
||||
export const handleAttach = async (req: any, res: any) =>
|
||||
routeHandler({
|
||||
@@ -20,8 +21,6 @@ export const handleAttach = async (req: any, res: any) =>
|
||||
|
||||
const attachBody = AttachBodySchema.parse(req.body);
|
||||
|
||||
const logger = req.logtail;
|
||||
|
||||
const { attachParams, customPrices, customEnts } = await getAttachParams({
|
||||
req,
|
||||
attachBody,
|
||||
|
||||
17
server/src/internal/customers/cusCache/cusCacheUtils.ts
Normal file
17
server/src/internal/customers/cusCache/cusCacheUtils.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
export const buildBaseCusCacheKey = ({
|
||||
idOrInternalId,
|
||||
entityId,
|
||||
orgId,
|
||||
env,
|
||||
}: {
|
||||
idOrInternalId: string;
|
||||
entityId?: string;
|
||||
orgId: string;
|
||||
env: string;
|
||||
}) => {
|
||||
if (entityId) {
|
||||
return `customer:${idOrInternalId}_${orgId}_${env}:${entityId}`;
|
||||
} else {
|
||||
return `customer:${idOrInternalId}_${orgId}_${env}`;
|
||||
}
|
||||
};
|
||||
83
server/src/internal/customers/cusCache/getCusWithCache.ts
Normal file
83
server/src/internal/customers/cusCache/getCusWithCache.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { AppEnv, CusExpand, EntityExpand, FullCustomer } from "@autumn/shared";
|
||||
import { ACTIVE_STATUSES } from "../cusProducts/CusProductService.js";
|
||||
import { db } from "@/db/initDrizzle.js";
|
||||
import { CusService } from "../CusService.js";
|
||||
import { buildBaseCusCacheKey } from "./cusCacheUtils.js";
|
||||
import { initUpstash } from "./upstashUtils.js";
|
||||
|
||||
export const getCusWithCache = async ({
|
||||
idOrInternalId,
|
||||
orgId,
|
||||
env,
|
||||
entityId,
|
||||
expand = [],
|
||||
allowNotFound = true,
|
||||
skipCache = false,
|
||||
skipGet = false,
|
||||
}: {
|
||||
idOrInternalId: string;
|
||||
orgId: string;
|
||||
env: AppEnv;
|
||||
entityId?: string;
|
||||
|
||||
// Optional
|
||||
expand?: (CusExpand | EntityExpand)[];
|
||||
allowNotFound?: boolean;
|
||||
skipCache?: boolean;
|
||||
skipGet?: boolean;
|
||||
}): Promise<FullCustomer> => {
|
||||
const statuses = ACTIVE_STATUSES;
|
||||
const withEntities = true;
|
||||
const withSubs = true;
|
||||
|
||||
const upstash = await initUpstash();
|
||||
if (!upstash) skipCache = true;
|
||||
|
||||
const baseKey = buildBaseCusCacheKey({
|
||||
idOrInternalId,
|
||||
orgId,
|
||||
env,
|
||||
entityId,
|
||||
});
|
||||
|
||||
const cacheKey = `${baseKey}:${expand.join(",")}`;
|
||||
if (!skipCache && !skipGet) {
|
||||
try {
|
||||
const cached = await upstash!.get(cacheKey);
|
||||
if (cached) {
|
||||
console.log(`Cache hit: ${cacheKey}`);
|
||||
return cached as FullCustomer;
|
||||
} else {
|
||||
console.log(`Cache miss: ${cacheKey}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
const customer = await CusService.getFull({
|
||||
db,
|
||||
idOrInternalId,
|
||||
orgId,
|
||||
env,
|
||||
inStatuses: statuses,
|
||||
withEntities,
|
||||
withSubs,
|
||||
allowNotFound,
|
||||
expand,
|
||||
entityId,
|
||||
});
|
||||
|
||||
if (entityId && !customer.entity) skipCache = true;
|
||||
|
||||
if (!skipCache) {
|
||||
try {
|
||||
await upstash!.set(cacheKey, customer);
|
||||
await upstash!.expire(cacheKey, 1000); // Expire after 60 seconds
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
return customer;
|
||||
};
|
||||
43
server/src/internal/customers/cusCache/updateCachedCus.ts
Normal file
43
server/src/internal/customers/cusCache/updateCachedCus.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { CusExpand, FullCusEntWithFullCusProduct } from "@autumn/shared";
|
||||
import { AppEnv } from "autumn-js";
|
||||
import { buildBaseCusCacheKey } from "./cusCacheUtils.js";
|
||||
import { getCusWithCache } from "./getCusWithCache.js";
|
||||
import { initUpstash } from "./upstashUtils.js";
|
||||
|
||||
export const refreshCusCache = async ({
|
||||
customerId,
|
||||
entityId,
|
||||
orgId,
|
||||
env,
|
||||
}: {
|
||||
customerId: string;
|
||||
entityId?: string;
|
||||
orgId: string;
|
||||
env: AppEnv;
|
||||
}) => {
|
||||
const upstash = await initUpstash();
|
||||
if (!upstash) return;
|
||||
|
||||
const baseKey = buildBaseCusCacheKey({
|
||||
idOrInternalId: customerId,
|
||||
orgId,
|
||||
env,
|
||||
});
|
||||
|
||||
const list = await upstash.keys(`${baseKey}:*`);
|
||||
|
||||
for (const key of list) {
|
||||
const keyName = key;
|
||||
let params = keyName.split(":");
|
||||
let expand = params ? params[params.length - 1].split(",") : [];
|
||||
|
||||
await getCusWithCache({
|
||||
idOrInternalId: customerId,
|
||||
orgId,
|
||||
env,
|
||||
expand: expand as CusExpand[],
|
||||
skipGet: true,
|
||||
});
|
||||
console.log(`updated cache key: ${keyName}`);
|
||||
}
|
||||
};
|
||||
13
server/src/internal/customers/cusCache/upstashUtils.ts
Normal file
13
server/src/internal/customers/cusCache/upstashUtils.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import "dotenv/config";
|
||||
import { Redis } from "@upstash/redis";
|
||||
|
||||
export const initUpstash = async () => {
|
||||
if (!process.env.UPSTASH_TOKEN) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Redis({
|
||||
url: "https://divine-sheepdog-46319.upstash.io",
|
||||
token: process.env.UPSTASH_TOKEN,
|
||||
});
|
||||
};
|
||||
@@ -7,6 +7,7 @@ import { ExtendedRequest, ExtendedResponse } from "@/utils/models/Request.js";
|
||||
import { routeHandler } from "@/utils/routerUtils.js";
|
||||
import { AppEnv, ErrCode, Organization } from "@autumn/shared";
|
||||
import { StatusCodes } from "http-status-codes";
|
||||
import { refreshCusCache } from "../cusCache/updateCachedCus.js";
|
||||
|
||||
export const deleteCusById = async ({
|
||||
db,
|
||||
@@ -55,7 +56,7 @@ export const deleteCusById = async ({
|
||||
`Couldn't delete ${chalk.yellow("stripe customer")} ${
|
||||
customer.processor.id
|
||||
}`,
|
||||
error?.message || error,
|
||||
error?.message || error
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -90,6 +91,12 @@ export const handleDeleteCustomer = async (req: any, res: any) =>
|
||||
deleteInStripe: req.query.delete_in_stripe === "true",
|
||||
});
|
||||
|
||||
await refreshCusCache({
|
||||
customerId: req.params.customer_id,
|
||||
orgId: org.id,
|
||||
env,
|
||||
});
|
||||
|
||||
res.status(200).json(data);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@ import { StatusCodes } from "http-status-codes";
|
||||
import { getCustomerDetails } from "../cusUtils/getCustomerDetails.js";
|
||||
import { parseCusExpand } from "../cusUtils/cusUtils.js";
|
||||
import { orgToVersion } from "@/utils/versionUtils.js";
|
||||
import { getCusWithCache } from "../cusCache/getCusWithCache.js";
|
||||
|
||||
export const handleGetCustomer = async (req: any, res: any) =>
|
||||
routeHandler({
|
||||
@@ -35,21 +36,23 @@ export const handleGetCustomer = async (req: any, res: any) =>
|
||||
|
||||
logger.info(`getting customer ${customerId} for org ${org.slug}`);
|
||||
const startTime = Date.now();
|
||||
const customer = await CusService.getFull({
|
||||
db,
|
||||
const customer = await getCusWithCache({
|
||||
idOrInternalId: customerId,
|
||||
orgId: org.id,
|
||||
env: env,
|
||||
inStatuses: [
|
||||
CusProductStatus.Active,
|
||||
CusProductStatus.PastDue,
|
||||
CusProductStatus.Scheduled,
|
||||
],
|
||||
withEntities: true,
|
||||
env,
|
||||
expand: expandArray,
|
||||
allowNotFound: true,
|
||||
withSubs: true,
|
||||
});
|
||||
// const customer = await CusService.getFull({
|
||||
// db,
|
||||
// idOrInternalId: customerId,
|
||||
// orgId: org.id,
|
||||
// env: env,
|
||||
// withEntities: true,
|
||||
// expand: expandArray,
|
||||
// allowNotFound: true,
|
||||
// withSubs: true,
|
||||
// });
|
||||
logger.info(`get customer took ${Date.now() - startTime}ms`);
|
||||
|
||||
if (!customer) {
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
} from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js";
|
||||
import { CusEntService } from "@/internal/customers/cusProducts/cusEnts/CusEntitlementService.js";
|
||||
import { notNullish } from "@/utils/genUtils.js";
|
||||
import { refreshCusCache } from "../cusCache/updateCachedCus.js";
|
||||
|
||||
const getCusFeaturesAndOrg = async (req: any, customerId: string) => {
|
||||
// 1. Get customer
|
||||
@@ -63,7 +64,7 @@ export const handleUpdateBalances = async (req: any, res: any) => {
|
||||
const { customer, org } = await getCusFeaturesAndOrg(req, cusId);
|
||||
|
||||
const featuresToUpdate = features.filter((f: any) =>
|
||||
balances.map((b: any) => b.feature_id).includes(f.id),
|
||||
balances.map((b: any) => b.feature_id).includes(f.id)
|
||||
);
|
||||
|
||||
if (featuresToUpdate.length === 0) {
|
||||
@@ -82,13 +83,12 @@ export const handleUpdateBalances = async (req: any, res: any) => {
|
||||
|
||||
logger.info("--------------------------------");
|
||||
logger.info(
|
||||
`REQUEST: UPDATE BALANCES FOR CUSTOMER ${customer.id}, ORG: ${org.slug}`,
|
||||
`REQUEST: UPDATE BALANCES FOR CUSTOMER ${customer.id}, ORG: ${org.slug}`
|
||||
);
|
||||
logger.info(
|
||||
`Features to update: ${balances.map(
|
||||
(b: any) =>
|
||||
`${b.feature_id} - ${b.unlimited ? "unlimited" : b.balance}`,
|
||||
)}`,
|
||||
(b: any) => `${b.feature_id} - ${b.unlimited ? "unlimited" : b.balance}`
|
||||
)}`
|
||||
);
|
||||
|
||||
// Get deductions for each feature
|
||||
@@ -111,7 +111,7 @@ export const handleUpdateBalances = async (req: any, res: any) => {
|
||||
}
|
||||
|
||||
const feature = featuresToUpdate.find(
|
||||
(f: any) => f.id === balance.feature_id,
|
||||
(f: any) => f.id === balance.feature_id
|
||||
);
|
||||
|
||||
if (balance.unlimited === true) {
|
||||
@@ -191,18 +191,17 @@ export const handleUpdateBalances = async (req: any, res: any) => {
|
||||
? cusEnts.find(
|
||||
(cusEnt) =>
|
||||
cusEnt.internal_feature_id === feature!.internal_id! &&
|
||||
cusEnt.entitlement.interval === interval,
|
||||
cusEnt.entitlement.interval === interval
|
||||
)
|
||||
: cusEnts.find(
|
||||
(cusEnt) =>
|
||||
cusEnt.internal_feature_id === feature!.internal_id!,
|
||||
(cusEnt) => cusEnt.internal_feature_id === feature!.internal_id!
|
||||
);
|
||||
|
||||
if (!cusEnt) {
|
||||
logger.warn(
|
||||
`No active cus ent to set unlimited balance for feature: ${
|
||||
feature!.id
|
||||
}`,
|
||||
}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -265,6 +264,13 @@ export const handleUpdateBalances = async (req: any, res: any) => {
|
||||
batchDeduct.push(performDeduction());
|
||||
}
|
||||
await Promise.all(batchDeduct);
|
||||
|
||||
await refreshCusCache({
|
||||
customerId: cusId,
|
||||
orgId: org.id,
|
||||
env,
|
||||
});
|
||||
|
||||
logger.info(" ✅ Successfully updated balances");
|
||||
|
||||
res.status(200).json({ success: true });
|
||||
|
||||
@@ -11,6 +11,7 @@ import { parseCusExpand } from "../cusUtils/cusUtils.js";
|
||||
import { FeatureService } from "@/internal/features/FeatureService.js";
|
||||
import { ExtendedResponse } from "@/utils/models/Request.js";
|
||||
import { ExtendedRequest } from "@/utils/models/Request.js";
|
||||
import { refreshCusCache } from "../cusCache/updateCachedCus.js";
|
||||
|
||||
export const handleUpdateCustomer = async (req: any, res: any) =>
|
||||
routeHandler({
|
||||
@@ -97,7 +98,7 @@ export const handleUpdateCustomer = async (req: any, res: any) =>
|
||||
const stripeCli = createStripeCli({ org, env: req.env });
|
||||
await stripeCli.customers.update(
|
||||
originalCustomer.processor.id,
|
||||
stripeUpdate as any,
|
||||
stripeUpdate as any
|
||||
);
|
||||
}
|
||||
|
||||
@@ -134,6 +135,12 @@ export const handleUpdateCustomer = async (req: any, res: any) =>
|
||||
reqApiVersion: req.apiVersion,
|
||||
});
|
||||
|
||||
await refreshCusCache({
|
||||
customerId,
|
||||
orgId,
|
||||
env,
|
||||
});
|
||||
|
||||
res.status(200).json(customerDetails);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -14,6 +14,7 @@ import { performDeductionOnCusEnt } from "@/trigger/updateBalanceTask.js";
|
||||
import { ExtendedRequest } from "@/utils/models/Request.js";
|
||||
import { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { CusProductService } from "../cusProducts/CusProductService.js";
|
||||
import { refreshCusCache } from "../cusCache/updateCachedCus.js";
|
||||
|
||||
const getCusOrgAndCusPrice = async ({
|
||||
db,
|
||||
|
||||
@@ -56,7 +56,7 @@ export const createEntities = async ({
|
||||
internalCustomerId: customer.internal_id,
|
||||
orgId: org.id,
|
||||
env,
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
let newEntities: Entity[] = [];
|
||||
|
||||
47
server/src/middleware/refreshCacheMiddleware.ts
Normal file
47
server/src/middleware/refreshCacheMiddleware.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
const urls = [
|
||||
{
|
||||
method: "POST",
|
||||
url: "/customers/:customer_id",
|
||||
},
|
||||
{
|
||||
method: "DELETE",
|
||||
url: "/customers/:customer_id",
|
||||
},
|
||||
{
|
||||
method: "POST",
|
||||
url: "/customers/:customer_id/balances",
|
||||
},
|
||||
{
|
||||
method: "POST",
|
||||
url: "/customers/customer_entitlements/:customer_entitlement_id",
|
||||
},
|
||||
{
|
||||
method: "POST",
|
||||
url: "/customers/:customer_id/balances",
|
||||
},
|
||||
{
|
||||
method: "POST",
|
||||
url: "/customers/:customer_id/coupons/:coupon_id",
|
||||
},
|
||||
{
|
||||
method: "POST",
|
||||
url: "/customers/:customer_id/entities",
|
||||
},
|
||||
{
|
||||
method: "POST",
|
||||
url: "/customers/:customer_id/transfer_product",
|
||||
},
|
||||
{
|
||||
method: "POST",
|
||||
url: "/attach",
|
||||
},
|
||||
];
|
||||
export const refreshCacheMiddleware = async (req: any, res: any, next: any) => {
|
||||
res.on("finish", async () => {
|
||||
console.log("URL:", req.originalUrl);
|
||||
console.log("METHOD:", req.method);
|
||||
console.log("--------------------------------");
|
||||
});
|
||||
|
||||
next();
|
||||
};
|
||||
@@ -42,6 +42,7 @@ import {
|
||||
getBillingType,
|
||||
getEntOptions,
|
||||
} from "@/internal/products/prices/priceUtils.js";
|
||||
import { refreshCusCache } from "@/internal/customers/cusCache/updateCachedCus.js";
|
||||
|
||||
// Decimal.set({ precision: 12 }); // 12 DP precision
|
||||
|
||||
@@ -67,7 +68,7 @@ const getFeatureDeductions = ({
|
||||
features: Feature[];
|
||||
}) => {
|
||||
const meteredFeatures = features.filter(
|
||||
(feature) => feature.type === FeatureType.Metered,
|
||||
(feature) => feature.type === FeatureType.Metered
|
||||
);
|
||||
const featureDeductions = [];
|
||||
for (const feature of features) {
|
||||
@@ -86,7 +87,7 @@ const getFeatureDeductions = ({
|
||||
let unlimitedExists = cusEnts.some(
|
||||
(cusEnt) =>
|
||||
cusEnt.entitlement.allowance_type === AllowanceType.Unlimited &&
|
||||
cusEnt.entitlement.internal_feature_id == feature.internal_id,
|
||||
cusEnt.entitlement.internal_feature_id == feature.internal_id
|
||||
);
|
||||
|
||||
if (unlimitedExists || !deduction) {
|
||||
@@ -142,7 +143,7 @@ export const logBalanceUpdate = ({
|
||||
console.log(
|
||||
` - Customer: ${customer.id} (${customer.env}) | Org: ${
|
||||
org.slug
|
||||
} | Features: ${features.map((f) => f.id).join(", ")}`,
|
||||
} | Features: ${features.map((f) => f.id).join(", ")}`
|
||||
);
|
||||
console.log(" - Properties:", properties);
|
||||
console.log(
|
||||
@@ -152,7 +153,7 @@ export const logBalanceUpdate = ({
|
||||
|
||||
if (notNullish(cusEnt.entitlement.entity_feature_id)) {
|
||||
console.log(
|
||||
` - Entity feature ID found for feature: ${cusEnt.feature_id}`,
|
||||
` - Entity feature ID found for feature: ${cusEnt.feature_id}`
|
||||
);
|
||||
|
||||
if (notNullish(entityId)) {
|
||||
@@ -179,7 +180,7 @@ export const logBalanceUpdate = ({
|
||||
})`;
|
||||
}),
|
||||
"| Deductions:",
|
||||
featureDeductions.map((f: any) => `${f.feature.id}: ${f.deduction}`),
|
||||
featureDeductions.map((f: any) => `${f.feature.id}: ${f.deduction}`)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -462,7 +463,7 @@ export const deductFromUsageBasedCusEnt = async ({
|
||||
|
||||
if (!usageBasedEnt) {
|
||||
console.log(
|
||||
` - Feature ${feature.id}, To deduct: ${toDeduct} -> no usage-based entitlement found`,
|
||||
` - Feature ${feature.id}, To deduct: ${toDeduct} -> no usage-based entitlement found`
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -659,7 +660,7 @@ export const runUpdateBalanceTask = async ({
|
||||
|
||||
console.log("--------------------------------");
|
||||
console.log(
|
||||
`UPDATING BALANCE FOR CUSTOMER (${customerId}), ORG: ${org.slug}`,
|
||||
`UPDATING BALANCE FOR CUSTOMER (${customerId}), ORG: ${org.slug}`
|
||||
);
|
||||
|
||||
const cusEnts: any = await updateCustomerBalance({
|
||||
@@ -673,6 +674,13 @@ export const runUpdateBalanceTask = async ({
|
||||
entityId,
|
||||
});
|
||||
|
||||
await refreshCusCache({
|
||||
customerId,
|
||||
orgId: org.id,
|
||||
env,
|
||||
entityId,
|
||||
});
|
||||
|
||||
if (!cusEnts || cusEnts.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
} from "./updateBalanceTask.js";
|
||||
import { CusService } from "@/internal/customers/CusService.js";
|
||||
import { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { refreshCusCache } from "@/internal/customers/cusCache/updateCachedCus.js";
|
||||
|
||||
// 2. Get deductions for each feature
|
||||
const getFeatureDeductions = ({
|
||||
@@ -42,7 +43,7 @@ const getFeatureDeductions = ({
|
||||
let unlimitedExists = cusEnts.some(
|
||||
(cusEnt) =>
|
||||
cusEnt.entitlement.allowance_type === AllowanceType.Unlimited &&
|
||||
cusEnt.entitlement.internal_feature_id == feature.internal_id,
|
||||
cusEnt.entitlement.internal_feature_id == feature.internal_id
|
||||
);
|
||||
|
||||
if (unlimitedExists) {
|
||||
@@ -129,7 +130,7 @@ const logUsageUpdate = ({
|
||||
org.slug
|
||||
} | Features: ${features.map((f) => f.id).join(", ")} | Set Usage: ${
|
||||
setUsage ? "true" : "false"
|
||||
}`,
|
||||
}`
|
||||
);
|
||||
|
||||
console.log(
|
||||
@@ -153,7 +154,7 @@ const logUsageUpdate = ({
|
||||
})`;
|
||||
}),
|
||||
"| Deductions:",
|
||||
featureDeductions.map((f: any) => `${f.feature.id}: ${f.deduction}`),
|
||||
featureDeductions.map((f: any) => `${f.feature.id}: ${f.deduction}`)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -300,7 +301,7 @@ export const runUpdateUsageTask = async ({
|
||||
|
||||
console.log("--------------------------------");
|
||||
console.log(
|
||||
`HANDLING USAGE TASK FOR CUSTOMER (${customerId}), ORG: ${org.slug}`,
|
||||
`HANDLING USAGE TASK FOR CUSTOMER (${customerId}), ORG: ${org.slug}`
|
||||
);
|
||||
|
||||
const cusEnts: any = await updateUsage({
|
||||
@@ -316,6 +317,13 @@ export const runUpdateUsageTask = async ({
|
||||
entityId,
|
||||
});
|
||||
|
||||
await refreshCusCache({
|
||||
customerId,
|
||||
entityId,
|
||||
orgId: org.id,
|
||||
env,
|
||||
});
|
||||
|
||||
if (!cusEnts || cusEnts.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user