mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 05:19:49 +00:00
feat: billing fixes
This commit is contained in:
@@ -48,9 +48,9 @@ import { registerSshCertRouter } from "./ssh-certificate-router";
|
||||
import { registerSshCertificateTemplateRouter } from "./ssh-certificate-template-router";
|
||||
import { registerSshHostGroupRouter } from "./ssh-host-group-router";
|
||||
import { registerSshHostRouter } from "./ssh-host-router";
|
||||
import { registerSubOrgRouter } from "./sub-org-router";
|
||||
import { registerTrustedIpRouter } from "./trusted-ip-router";
|
||||
import { registerUserAdditionalPrivilegeRouter } from "./user-additional-privilege-router";
|
||||
import { registerSubOrgRouter } from "./sub-org-router";
|
||||
|
||||
export const registerV1EERoutes = async (server: FastifyZodProvider) => {
|
||||
// org role starts with organization
|
||||
|
||||
@@ -10,6 +10,7 @@ export const licenseDALFactory = (db: TDbClient) => {
|
||||
const countOfOrgMembers = async (orgId: string | null, tx?: Knex) => {
|
||||
try {
|
||||
const doc = await (tx || db.replicaNode())(TableName.Membership)
|
||||
.join(TableName.Organization, `${TableName.Organization}.id`, `${TableName.Membership}.scopeOrgId`)
|
||||
.where({ status: OrgMembershipStatus.Accepted, scope: AccessScope.Organization })
|
||||
.andWhere((bd) => {
|
||||
if (orgId) {
|
||||
@@ -18,6 +19,7 @@ export const licenseDALFactory = (db: TDbClient) => {
|
||||
})
|
||||
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
|
||||
.where(`${TableName.Users}.isGhost`, false)
|
||||
.whereNull(`${TableName.Organization}.rootOrgId`)
|
||||
.count();
|
||||
return Number(doc?.[0]?.count ?? 0);
|
||||
} catch (error) {
|
||||
@@ -25,10 +27,31 @@ export const licenseDALFactory = (db: TDbClient) => {
|
||||
}
|
||||
};
|
||||
|
||||
const countOfOrgIdentities = async (orgId: string | null, tx?: Knex) => {
|
||||
try {
|
||||
// count org identities
|
||||
const identityDoc = await (tx || db.replicaNode())(TableName.Identity)
|
||||
.join(TableName.Organization, `${TableName.Identity}.orgId`, `${TableName.Organization}.id`)
|
||||
.where((bd) => {
|
||||
if (orgId) {
|
||||
void bd.where(`${TableName.Organization}.rootOrgId`, orgId).orWhere(`${TableName.Organization}.id`, orgId);
|
||||
}
|
||||
})
|
||||
.count();
|
||||
|
||||
const identityCount = Number(identityDoc?.[0].count);
|
||||
|
||||
return identityCount;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Count of Org Users + Identities" });
|
||||
}
|
||||
};
|
||||
|
||||
const countOrgUsersAndIdentities = async (orgId: string | null, tx?: Knex) => {
|
||||
try {
|
||||
// count org users
|
||||
const userDoc = await (tx || db.replicaNode())(TableName.Membership)
|
||||
.join(TableName.Organization, `${TableName.Organization}.id`, `${TableName.Membership}.scopeOrgId`)
|
||||
.where({ status: OrgMembershipStatus.Accepted, scope: AccessScope.Organization })
|
||||
.whereNotNull(`${TableName.Membership}.actorUserId`)
|
||||
.andWhere((bd) => {
|
||||
@@ -38,17 +61,17 @@ export const licenseDALFactory = (db: TDbClient) => {
|
||||
})
|
||||
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
|
||||
.where(`${TableName.Users}.isGhost`, false)
|
||||
.whereNull(`${TableName.Organization}.rootOrgId`)
|
||||
.count();
|
||||
|
||||
const userCount = Number(userDoc?.[0].count);
|
||||
|
||||
// count org identities
|
||||
const identityDoc = await (tx || db.replicaNode())(TableName.Membership)
|
||||
.where({ scope: AccessScope.Organization })
|
||||
.whereNotNull(`${TableName.Membership}.actorIdentityId`)
|
||||
const identityDoc = await (tx || db.replicaNode())(TableName.Identity)
|
||||
.join(TableName.Organization, `${TableName.Identity}.orgId`, `${TableName.Organization}.id`)
|
||||
.where((bd) => {
|
||||
if (orgId) {
|
||||
void bd.where(`${TableName.Membership}.scopeOrgId`, orgId);
|
||||
void bd.where(`${TableName.Organization}.rootOrgId`, orgId).orWhere(`${TableName.Organization}.id`, orgId);
|
||||
}
|
||||
})
|
||||
.count();
|
||||
@@ -61,5 +84,5 @@ export const licenseDALFactory = (db: TDbClient) => {
|
||||
}
|
||||
};
|
||||
|
||||
return { countOfOrgMembers, countOrgUsersAndIdentities };
|
||||
return { countOfOrgMembers, countOrgUsersAndIdentities, countOfOrgIdentities };
|
||||
};
|
||||
|
||||
@@ -15,7 +15,6 @@ import { getConfig } from "@app/lib/config/env";
|
||||
import { verifyOfflineLicense } from "@app/lib/crypto";
|
||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { TIdentityOrgDALFactory } from "@app/services/identity/identity-org-dal";
|
||||
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||
|
||||
@@ -46,11 +45,10 @@ import {
|
||||
} from "./license-types";
|
||||
|
||||
type TLicenseServiceFactoryDep = {
|
||||
orgDAL: Pick<TOrgDALFactory, "findOrgById" | "countAllOrgMembers">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findRootOrgDetails" | "countAllOrgMembers" | "findById">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
licenseDAL: TLicenseDALFactory;
|
||||
keyStore: Pick<TKeyStoreFactory, "setItemWithExpiry" | "getItem" | "deleteItem">;
|
||||
identityOrgMembershipDAL: TIdentityOrgDALFactory;
|
||||
projectDAL: TProjectDALFactory;
|
||||
};
|
||||
|
||||
@@ -67,7 +65,6 @@ export const licenseServiceFactory = ({
|
||||
permissionService,
|
||||
licenseDAL,
|
||||
keyStore,
|
||||
identityOrgMembershipDAL,
|
||||
projectDAL
|
||||
}: TLicenseServiceFactoryDep) => {
|
||||
let isValidLicense = false;
|
||||
@@ -200,19 +197,21 @@ export const licenseServiceFactory = ({
|
||||
return JSON.parse(cachedPlan) as TFeatureSet;
|
||||
}
|
||||
|
||||
const org = await orgDAL.findOrgById(orgId);
|
||||
const org = await orgDAL.findRootOrgDetails(orgId);
|
||||
if (!org) throw new NotFoundError({ message: `Organization with ID '${orgId}' not found` });
|
||||
const rootOrgId = org.id;
|
||||
|
||||
const {
|
||||
data: { currentPlan }
|
||||
} = await licenseServerCloudApi.request.get<{ currentPlan: TFeatureSet }>(
|
||||
`/api/license-server/v1/customers/${org.customerId}/cloud-plan`
|
||||
);
|
||||
const workspacesUsed = await projectDAL.countOfOrgProjects(orgId);
|
||||
const workspacesUsed = await projectDAL.countOfOrgProjects(rootOrgId);
|
||||
currentPlan.workspacesUsed = workspacesUsed;
|
||||
|
||||
const membersUsed = await licenseDAL.countOfOrgMembers(orgId);
|
||||
const membersUsed = await licenseDAL.countOfOrgMembers(rootOrgId);
|
||||
currentPlan.membersUsed = membersUsed;
|
||||
const identityUsed = await licenseDAL.countOrgUsersAndIdentities(orgId);
|
||||
const identityUsed = await licenseDAL.countOrgUsersAndIdentities(rootOrgId);
|
||||
currentPlan.identitiesUsed = identityUsed;
|
||||
|
||||
if (currentPlan.identityLimit && currentPlan.identityLimit !== identityUsed) {
|
||||
@@ -285,10 +284,10 @@ export const licenseServiceFactory = ({
|
||||
};
|
||||
|
||||
const updateSubscriptionOrgMemberCount = async (orgId: string, tx?: Knex) => {
|
||||
const org = await orgDAL.findOrgById(orgId);
|
||||
const org = await orgDAL.findRootOrgDetails(orgId);
|
||||
if (!org) throw new NotFoundError({ message: `Organization with ID '${orgId}' not found` });
|
||||
|
||||
const rootOrgId = org.rootOrgId || org.id;
|
||||
const rootOrgId = org.id;
|
||||
if (instanceType === InstanceType.Cloud) {
|
||||
const quantity = await licenseDAL.countOfOrgMembers(rootOrgId, tx);
|
||||
const quantityIdentities = await licenseDAL.countOrgUsersAndIdentities(rootOrgId, tx);
|
||||
@@ -381,7 +380,7 @@ export const licenseServiceFactory = ({
|
||||
OrgPermissionSubjects.Billing
|
||||
);
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
const organization = await orgDAL.findById(orgId);
|
||||
if (!organization) {
|
||||
throw new NotFoundError({
|
||||
message: `Organization with ID '${orgId}' not found`
|
||||
@@ -420,7 +419,7 @@ export const licenseServiceFactory = ({
|
||||
OrgPermissionSubjects.Billing
|
||||
);
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
const organization = await orgDAL.findById(orgId);
|
||||
if (!organization) {
|
||||
throw new NotFoundError({
|
||||
message: "Organization not found"
|
||||
@@ -473,7 +472,7 @@ export const licenseServiceFactory = ({
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
const organization = await orgDAL.findById(orgId);
|
||||
if (!organization) {
|
||||
throw new NotFoundError({
|
||||
message: `Organization with ID '${orgId}' not found`
|
||||
@@ -539,7 +538,7 @@ export const licenseServiceFactory = ({
|
||||
const getUsageMetrics = async (orgId: string) => {
|
||||
const [orgMembersUsed, identityUsed, projectCount] = await Promise.all([
|
||||
orgDAL.countAllOrgMembers(orgId),
|
||||
identityOrgMembershipDAL.countAllOrgIdentities({ scopeOrgId: orgId }),
|
||||
licenseDAL.countOfOrgIdentities(orgId),
|
||||
projectDAL.countOfOrgProjects(orgId)
|
||||
]);
|
||||
|
||||
@@ -563,7 +562,7 @@ export const licenseServiceFactory = ({
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
const organization = await orgDAL.findById(orgId);
|
||||
if (!organization) {
|
||||
throw new NotFoundError({
|
||||
message: `Organization with ID '${orgId}' not found`
|
||||
@@ -607,7 +606,7 @@ export const licenseServiceFactory = ({
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
const organization = await orgDAL.findById(orgId);
|
||||
if (!organization) {
|
||||
throw new NotFoundError({
|
||||
message: `Organization with ID '${orgId}' not found`
|
||||
@@ -642,7 +641,7 @@ export const licenseServiceFactory = ({
|
||||
OrgPermissionSubjects.Billing
|
||||
);
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
const organization = await orgDAL.findById(orgId);
|
||||
if (!organization) {
|
||||
throw new NotFoundError({
|
||||
message: `Organization with ID '${orgId}' not found`
|
||||
@@ -669,7 +668,7 @@ export const licenseServiceFactory = ({
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
const organization = await orgDAL.findById(orgId);
|
||||
if (!organization) {
|
||||
throw new NotFoundError({
|
||||
message: `Organization with ID '${orgId}' not found`
|
||||
@@ -706,7 +705,7 @@ export const licenseServiceFactory = ({
|
||||
OrgPermissionSubjects.Billing
|
||||
);
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
const organization = await orgDAL.findById(orgId);
|
||||
if (!organization) {
|
||||
throw new NotFoundError({
|
||||
message: `Organization with ID '${orgId}' not found`
|
||||
@@ -745,7 +744,7 @@ export const licenseServiceFactory = ({
|
||||
OrgPermissionSubjects.Billing
|
||||
);
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
const organization = await orgDAL.findById(orgId);
|
||||
if (!organization) {
|
||||
throw new NotFoundError({
|
||||
message: `Organization with ID '${orgId}' not found`
|
||||
@@ -781,7 +780,7 @@ export const licenseServiceFactory = ({
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
const organization = await orgDAL.findById(orgId);
|
||||
if (!organization) {
|
||||
throw new NotFoundError({
|
||||
message: `Organization with ID '${orgId}' not found`
|
||||
@@ -809,7 +808,7 @@ export const licenseServiceFactory = ({
|
||||
OrgPermissionSubjects.Billing
|
||||
);
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
const organization = await orgDAL.findById(orgId);
|
||||
if (!organization) {
|
||||
throw new NotFoundError({
|
||||
message: `Organization with ID '${orgId}' not found`
|
||||
@@ -840,7 +839,7 @@ export const licenseServiceFactory = ({
|
||||
OrgPermissionSubjects.Billing
|
||||
);
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
const organization = await orgDAL.findById(orgId);
|
||||
if (!organization) {
|
||||
throw new NotFoundError({
|
||||
message: `Organization with ID '${orgId}' not found`
|
||||
@@ -864,7 +863,7 @@ export const licenseServiceFactory = ({
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
const organization = await orgDAL.findById(orgId);
|
||||
if (!organization) {
|
||||
throw new NotFoundError({
|
||||
message: `Organization with ID '${orgId}' not found`
|
||||
@@ -888,7 +887,7 @@ export const licenseServiceFactory = ({
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
const organization = await orgDAL.findById(orgId);
|
||||
if (!organization) {
|
||||
throw new NotFoundError({
|
||||
message: `Organization with ID '${orgId}' not found`
|
||||
@@ -933,7 +932,6 @@ export const licenseServiceFactory = ({
|
||||
getLicenseId,
|
||||
invalidateGetPlan,
|
||||
updateSubscriptionOrgMemberCount,
|
||||
refreshPlan,
|
||||
getOrgPlan,
|
||||
getOrgPlansTableByBillCycle,
|
||||
startOrgTrial,
|
||||
|
||||
Reference in New Issue
Block a user