mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 14:28:20 +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 { registerSshCertificateTemplateRouter } from "./ssh-certificate-template-router";
|
||||||
import { registerSshHostGroupRouter } from "./ssh-host-group-router";
|
import { registerSshHostGroupRouter } from "./ssh-host-group-router";
|
||||||
import { registerSshHostRouter } from "./ssh-host-router";
|
import { registerSshHostRouter } from "./ssh-host-router";
|
||||||
|
import { registerSubOrgRouter } from "./sub-org-router";
|
||||||
import { registerTrustedIpRouter } from "./trusted-ip-router";
|
import { registerTrustedIpRouter } from "./trusted-ip-router";
|
||||||
import { registerUserAdditionalPrivilegeRouter } from "./user-additional-privilege-router";
|
import { registerUserAdditionalPrivilegeRouter } from "./user-additional-privilege-router";
|
||||||
import { registerSubOrgRouter } from "./sub-org-router";
|
|
||||||
|
|
||||||
export const registerV1EERoutes = async (server: FastifyZodProvider) => {
|
export const registerV1EERoutes = async (server: FastifyZodProvider) => {
|
||||||
// org role starts with organization
|
// org role starts with organization
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export const licenseDALFactory = (db: TDbClient) => {
|
|||||||
const countOfOrgMembers = async (orgId: string | null, tx?: Knex) => {
|
const countOfOrgMembers = async (orgId: string | null, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const doc = await (tx || db.replicaNode())(TableName.Membership)
|
const doc = await (tx || db.replicaNode())(TableName.Membership)
|
||||||
|
.join(TableName.Organization, `${TableName.Organization}.id`, `${TableName.Membership}.scopeOrgId`)
|
||||||
.where({ status: OrgMembershipStatus.Accepted, scope: AccessScope.Organization })
|
.where({ status: OrgMembershipStatus.Accepted, scope: AccessScope.Organization })
|
||||||
.andWhere((bd) => {
|
.andWhere((bd) => {
|
||||||
if (orgId) {
|
if (orgId) {
|
||||||
@@ -18,6 +19,7 @@ export const licenseDALFactory = (db: TDbClient) => {
|
|||||||
})
|
})
|
||||||
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
|
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
|
||||||
.where(`${TableName.Users}.isGhost`, false)
|
.where(`${TableName.Users}.isGhost`, false)
|
||||||
|
.whereNull(`${TableName.Organization}.rootOrgId`)
|
||||||
.count();
|
.count();
|
||||||
return Number(doc?.[0]?.count ?? 0);
|
return Number(doc?.[0]?.count ?? 0);
|
||||||
} catch (error) {
|
} 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) => {
|
const countOrgUsersAndIdentities = async (orgId: string | null, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
// count org users
|
// count org users
|
||||||
const userDoc = await (tx || db.replicaNode())(TableName.Membership)
|
const userDoc = await (tx || db.replicaNode())(TableName.Membership)
|
||||||
|
.join(TableName.Organization, `${TableName.Organization}.id`, `${TableName.Membership}.scopeOrgId`)
|
||||||
.where({ status: OrgMembershipStatus.Accepted, scope: AccessScope.Organization })
|
.where({ status: OrgMembershipStatus.Accepted, scope: AccessScope.Organization })
|
||||||
.whereNotNull(`${TableName.Membership}.actorUserId`)
|
.whereNotNull(`${TableName.Membership}.actorUserId`)
|
||||||
.andWhere((bd) => {
|
.andWhere((bd) => {
|
||||||
@@ -38,17 +61,17 @@ export const licenseDALFactory = (db: TDbClient) => {
|
|||||||
})
|
})
|
||||||
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
|
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
|
||||||
.where(`${TableName.Users}.isGhost`, false)
|
.where(`${TableName.Users}.isGhost`, false)
|
||||||
|
.whereNull(`${TableName.Organization}.rootOrgId`)
|
||||||
.count();
|
.count();
|
||||||
|
|
||||||
const userCount = Number(userDoc?.[0].count);
|
const userCount = Number(userDoc?.[0].count);
|
||||||
|
|
||||||
// count org identities
|
// count org identities
|
||||||
const identityDoc = await (tx || db.replicaNode())(TableName.Membership)
|
const identityDoc = await (tx || db.replicaNode())(TableName.Identity)
|
||||||
.where({ scope: AccessScope.Organization })
|
.join(TableName.Organization, `${TableName.Identity}.orgId`, `${TableName.Organization}.id`)
|
||||||
.whereNotNull(`${TableName.Membership}.actorIdentityId`)
|
|
||||||
.where((bd) => {
|
.where((bd) => {
|
||||||
if (orgId) {
|
if (orgId) {
|
||||||
void bd.where(`${TableName.Membership}.scopeOrgId`, orgId);
|
void bd.where(`${TableName.Organization}.rootOrgId`, orgId).orWhere(`${TableName.Organization}.id`, orgId);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.count();
|
.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 { verifyOfflineLicense } from "@app/lib/crypto";
|
||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
import { TIdentityOrgDALFactory } from "@app/services/identity/identity-org-dal";
|
|
||||||
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
|
|
||||||
@@ -46,11 +45,10 @@ import {
|
|||||||
} from "./license-types";
|
} from "./license-types";
|
||||||
|
|
||||||
type TLicenseServiceFactoryDep = {
|
type TLicenseServiceFactoryDep = {
|
||||||
orgDAL: Pick<TOrgDALFactory, "findOrgById" | "countAllOrgMembers">;
|
orgDAL: Pick<TOrgDALFactory, "findRootOrgDetails" | "countAllOrgMembers" | "findById">;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
licenseDAL: TLicenseDALFactory;
|
licenseDAL: TLicenseDALFactory;
|
||||||
keyStore: Pick<TKeyStoreFactory, "setItemWithExpiry" | "getItem" | "deleteItem">;
|
keyStore: Pick<TKeyStoreFactory, "setItemWithExpiry" | "getItem" | "deleteItem">;
|
||||||
identityOrgMembershipDAL: TIdentityOrgDALFactory;
|
|
||||||
projectDAL: TProjectDALFactory;
|
projectDAL: TProjectDALFactory;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -67,7 +65,6 @@ export const licenseServiceFactory = ({
|
|||||||
permissionService,
|
permissionService,
|
||||||
licenseDAL,
|
licenseDAL,
|
||||||
keyStore,
|
keyStore,
|
||||||
identityOrgMembershipDAL,
|
|
||||||
projectDAL
|
projectDAL
|
||||||
}: TLicenseServiceFactoryDep) => {
|
}: TLicenseServiceFactoryDep) => {
|
||||||
let isValidLicense = false;
|
let isValidLicense = false;
|
||||||
@@ -200,19 +197,21 @@ export const licenseServiceFactory = ({
|
|||||||
return JSON.parse(cachedPlan) as TFeatureSet;
|
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` });
|
if (!org) throw new NotFoundError({ message: `Organization with ID '${orgId}' not found` });
|
||||||
|
const rootOrgId = org.id;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: { currentPlan }
|
data: { currentPlan }
|
||||||
} = await licenseServerCloudApi.request.get<{ currentPlan: TFeatureSet }>(
|
} = await licenseServerCloudApi.request.get<{ currentPlan: TFeatureSet }>(
|
||||||
`/api/license-server/v1/customers/${org.customerId}/cloud-plan`
|
`/api/license-server/v1/customers/${org.customerId}/cloud-plan`
|
||||||
);
|
);
|
||||||
const workspacesUsed = await projectDAL.countOfOrgProjects(orgId);
|
const workspacesUsed = await projectDAL.countOfOrgProjects(rootOrgId);
|
||||||
currentPlan.workspacesUsed = workspacesUsed;
|
currentPlan.workspacesUsed = workspacesUsed;
|
||||||
|
|
||||||
const membersUsed = await licenseDAL.countOfOrgMembers(orgId);
|
const membersUsed = await licenseDAL.countOfOrgMembers(rootOrgId);
|
||||||
currentPlan.membersUsed = membersUsed;
|
currentPlan.membersUsed = membersUsed;
|
||||||
const identityUsed = await licenseDAL.countOrgUsersAndIdentities(orgId);
|
const identityUsed = await licenseDAL.countOrgUsersAndIdentities(rootOrgId);
|
||||||
currentPlan.identitiesUsed = identityUsed;
|
currentPlan.identitiesUsed = identityUsed;
|
||||||
|
|
||||||
if (currentPlan.identityLimit && currentPlan.identityLimit !== identityUsed) {
|
if (currentPlan.identityLimit && currentPlan.identityLimit !== identityUsed) {
|
||||||
@@ -285,10 +284,10 @@ export const licenseServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateSubscriptionOrgMemberCount = async (orgId: string, tx?: Knex) => {
|
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` });
|
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) {
|
if (instanceType === InstanceType.Cloud) {
|
||||||
const quantity = await licenseDAL.countOfOrgMembers(rootOrgId, tx);
|
const quantity = await licenseDAL.countOfOrgMembers(rootOrgId, tx);
|
||||||
const quantityIdentities = await licenseDAL.countOrgUsersAndIdentities(rootOrgId, tx);
|
const quantityIdentities = await licenseDAL.countOrgUsersAndIdentities(rootOrgId, tx);
|
||||||
@@ -381,7 +380,7 @@ export const licenseServiceFactory = ({
|
|||||||
OrgPermissionSubjects.Billing
|
OrgPermissionSubjects.Billing
|
||||||
);
|
);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
@@ -420,7 +419,7 @@ export const licenseServiceFactory = ({
|
|||||||
OrgPermissionSubjects.Billing
|
OrgPermissionSubjects.Billing
|
||||||
);
|
);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: "Organization not found"
|
message: "Organization not found"
|
||||||
@@ -473,7 +472,7 @@ export const licenseServiceFactory = ({
|
|||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
@@ -539,7 +538,7 @@ export const licenseServiceFactory = ({
|
|||||||
const getUsageMetrics = async (orgId: string) => {
|
const getUsageMetrics = async (orgId: string) => {
|
||||||
const [orgMembersUsed, identityUsed, projectCount] = await Promise.all([
|
const [orgMembersUsed, identityUsed, projectCount] = await Promise.all([
|
||||||
orgDAL.countAllOrgMembers(orgId),
|
orgDAL.countAllOrgMembers(orgId),
|
||||||
identityOrgMembershipDAL.countAllOrgIdentities({ scopeOrgId: orgId }),
|
licenseDAL.countOfOrgIdentities(orgId),
|
||||||
projectDAL.countOfOrgProjects(orgId)
|
projectDAL.countOfOrgProjects(orgId)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -563,7 +562,7 @@ export const licenseServiceFactory = ({
|
|||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
@@ -607,7 +606,7 @@ export const licenseServiceFactory = ({
|
|||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
@@ -642,7 +641,7 @@ export const licenseServiceFactory = ({
|
|||||||
OrgPermissionSubjects.Billing
|
OrgPermissionSubjects.Billing
|
||||||
);
|
);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
@@ -669,7 +668,7 @@ export const licenseServiceFactory = ({
|
|||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
@@ -706,7 +705,7 @@ export const licenseServiceFactory = ({
|
|||||||
OrgPermissionSubjects.Billing
|
OrgPermissionSubjects.Billing
|
||||||
);
|
);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
@@ -745,7 +744,7 @@ export const licenseServiceFactory = ({
|
|||||||
OrgPermissionSubjects.Billing
|
OrgPermissionSubjects.Billing
|
||||||
);
|
);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
@@ -781,7 +780,7 @@ export const licenseServiceFactory = ({
|
|||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
@@ -809,7 +808,7 @@ export const licenseServiceFactory = ({
|
|||||||
OrgPermissionSubjects.Billing
|
OrgPermissionSubjects.Billing
|
||||||
);
|
);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
@@ -840,7 +839,7 @@ export const licenseServiceFactory = ({
|
|||||||
OrgPermissionSubjects.Billing
|
OrgPermissionSubjects.Billing
|
||||||
);
|
);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
@@ -864,7 +863,7 @@ export const licenseServiceFactory = ({
|
|||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
@@ -888,7 +887,7 @@ export const licenseServiceFactory = ({
|
|||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
@@ -933,7 +932,6 @@ export const licenseServiceFactory = ({
|
|||||||
getLicenseId,
|
getLicenseId,
|
||||||
invalidateGetPlan,
|
invalidateGetPlan,
|
||||||
updateSubscriptionOrgMemberCount,
|
updateSubscriptionOrgMemberCount,
|
||||||
refreshPlan,
|
|
||||||
getOrgPlan,
|
getOrgPlan,
|
||||||
getOrgPlansTableByBillCycle,
|
getOrgPlansTableByBillCycle,
|
||||||
startOrgTrial,
|
startOrgTrial,
|
||||||
|
|||||||
@@ -561,7 +561,6 @@ export const registerRoutes = async (
|
|||||||
orgDAL,
|
orgDAL,
|
||||||
licenseDAL,
|
licenseDAL,
|
||||||
keyStore,
|
keyStore,
|
||||||
identityOrgMembershipDAL,
|
|
||||||
projectDAL
|
projectDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import { registerIdentityKubernetesRouter } from "./identity-kubernetes-auth-rou
|
|||||||
import { registerIdentityLdapAuthRouter } from "./identity-ldap-auth-router";
|
import { registerIdentityLdapAuthRouter } from "./identity-ldap-auth-router";
|
||||||
import { registerIdentityOciAuthRouter } from "./identity-oci-auth-router";
|
import { registerIdentityOciAuthRouter } from "./identity-oci-auth-router";
|
||||||
import { registerIdentityOidcAuthRouter } from "./identity-oidc-auth-router";
|
import { registerIdentityOidcAuthRouter } from "./identity-oidc-auth-router";
|
||||||
|
import { registerOrgIdentityMembershipRouter } from "./identity-org-membership-router";
|
||||||
import { registerIdentityProjectRouter } from "./identity-project-router";
|
import { registerIdentityProjectRouter } from "./identity-project-router";
|
||||||
import { registerIdentityRouter } from "./identity-router";
|
import { registerIdentityRouter } from "./identity-router";
|
||||||
import { registerIdentityTlsCertAuthRouter } from "./identity-tls-cert-auth-router";
|
import { registerIdentityTlsCertAuthRouter } from "./identity-tls-cert-auth-router";
|
||||||
@@ -65,7 +66,6 @@ import { registerUserEngagementRouter } from "./user-engagement-router";
|
|||||||
import { registerUserRouter } from "./user-router";
|
import { registerUserRouter } from "./user-router";
|
||||||
import { registerWebhookRouter } from "./webhook-router";
|
import { registerWebhookRouter } from "./webhook-router";
|
||||||
import { registerWorkflowIntegrationRouter } from "./workflow-integration-router";
|
import { registerWorkflowIntegrationRouter } from "./workflow-integration-router";
|
||||||
import { registerOrgIdentityMembershipRouter } from "./identity-org-membership-router";
|
|
||||||
|
|
||||||
export const registerV1Routes = async (server: FastifyZodProvider) => {
|
export const registerV1Routes = async (server: FastifyZodProvider) => {
|
||||||
await server.register(registerSsoRouter, { prefix: "/sso" });
|
await server.register(registerSsoRouter, { prefix: "/sso" });
|
||||||
|
|||||||
@@ -654,7 +654,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
|||||||
tx?: Knex
|
tx?: Knex
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
const query = (tx || db.replicaNode())(TableName.Membership)
|
const query = (tx || db.replicaNode())(TableName.Identity)
|
||||||
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
|
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
|
||||||
.whereNotNull(`${TableName.Membership}.actorIdentityId`)
|
.whereNotNull(`${TableName.Membership}.actorIdentityId`)
|
||||||
.where(filter)
|
.where(filter)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { TKeyStoreFactory } from "@app/keystore/keystore";
|
|||||||
import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors";
|
||||||
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
|
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
|
||||||
|
|
||||||
|
import { TAdditionalPrivilegeDALFactory } from "../additional-privilege/additional-privilege-dal";
|
||||||
import { TMembershipRoleDALFactory } from "../membership/membership-role-dal";
|
import { TMembershipRoleDALFactory } from "../membership/membership-role-dal";
|
||||||
import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal";
|
import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal";
|
||||||
import { TOrgDALFactory } from "../org/org-dal";
|
import { TOrgDALFactory } from "../org/org-dal";
|
||||||
@@ -28,7 +29,6 @@ import {
|
|||||||
TSearchOrgIdentitiesByOrgIdDTO,
|
TSearchOrgIdentitiesByOrgIdDTO,
|
||||||
TUpdateIdentityDTO
|
TUpdateIdentityDTO
|
||||||
} from "./identity-types";
|
} from "./identity-types";
|
||||||
import { TAdditionalPrivilegeDALFactory } from "../additional-privilege/additional-privilege-dal";
|
|
||||||
|
|
||||||
type TIdentityServiceFactoryDep = {
|
type TIdentityServiceFactoryDep = {
|
||||||
identityDAL: TIdentityDALFactory;
|
identityDAL: TIdentityDALFactory;
|
||||||
@@ -347,6 +347,13 @@ export const identityServiceFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
await membershipIdentityDAL.transaction(async (tx) => {
|
await membershipIdentityDAL.transaction(async (tx) => {
|
||||||
|
await identityMetadataDAL.delete(
|
||||||
|
{
|
||||||
|
identityId: id,
|
||||||
|
orgId: actorOrgId
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
const identityProjectMembership = await membershipIdentityDAL.find(
|
const identityProjectMembership = await membershipIdentityDAL.find(
|
||||||
{
|
{
|
||||||
actorIdentityId: id,
|
actorIdentityId: id,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { ms } from "@app/lib/ms";
|
|||||||
import { SearchResourceOperators } from "@app/lib/search-resource/search";
|
import { SearchResourceOperators } from "@app/lib/search-resource/search";
|
||||||
|
|
||||||
import { TAdditionalPrivilegeDALFactory } from "../additional-privilege/additional-privilege-dal";
|
import { TAdditionalPrivilegeDALFactory } from "../additional-privilege/additional-privilege-dal";
|
||||||
|
import { TIdentityDALFactory } from "../identity/identity-dal";
|
||||||
import { TMembershipRoleDALFactory } from "../membership/membership-role-dal";
|
import { TMembershipRoleDALFactory } from "../membership/membership-role-dal";
|
||||||
import { TOrgDALFactory } from "../org/org-dal";
|
import { TOrgDALFactory } from "../org/org-dal";
|
||||||
import { TRoleDALFactory } from "../role/role-dal";
|
import { TRoleDALFactory } from "../role/role-dal";
|
||||||
@@ -20,7 +21,6 @@ import {
|
|||||||
import { newNamespaceMembershipIdentityFactory } from "./namespace/namespace-membership-identity-factory";
|
import { newNamespaceMembershipIdentityFactory } from "./namespace/namespace-membership-identity-factory";
|
||||||
import { newOrgMembershipIdentityFactory } from "./org/org-membership-identity-factory";
|
import { newOrgMembershipIdentityFactory } from "./org/org-membership-identity-factory";
|
||||||
import { newProjectMembershipIdentityFactory } from "./project/project-membership-identity-factory";
|
import { newProjectMembershipIdentityFactory } from "./project/project-membership-identity-factory";
|
||||||
import { TIdentityDALFactory } from "../identity/identity-dal";
|
|
||||||
|
|
||||||
type TMembershipIdentityServiceFactoryDep = {
|
type TMembershipIdentityServiceFactoryDep = {
|
||||||
membershipIdentityDAL: TMembershipIdentityDALFactory;
|
membershipIdentityDAL: TMembershipIdentityDALFactory;
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ import {
|
|||||||
} from "@app/ee/services/permission/permission-fns";
|
} from "@app/ee/services/permission/permission-fns";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import { BadRequestError, InternalServerError, PermissionBoundaryError } from "@app/lib/errors";
|
import { BadRequestError, InternalServerError, PermissionBoundaryError } from "@app/lib/errors";
|
||||||
|
import { TIdentityDALFactory } from "@app/services/identity/identity-dal";
|
||||||
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||||
import { isCustomOrgRole } from "@app/services/org/org-role-fns";
|
import { isCustomOrgRole } from "@app/services/org/org-role-fns";
|
||||||
|
|
||||||
import { TMembershipIdentityScopeFactory } from "../membership-identity-types";
|
import { TMembershipIdentityScopeFactory } from "../membership-identity-types";
|
||||||
import { TIdentityDALFactory } from "@app/services/identity/identity-dal";
|
|
||||||
|
|
||||||
type TOrgMembershipIdentityScopeFactoryDep = {
|
type TOrgMembershipIdentityScopeFactoryDep = {
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getOrgPermissionByRoles">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getOrgPermissionByRoles">;
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ import { isCustomOrgRole } from "@app/services/org/org-role-fns";
|
|||||||
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
||||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||||
|
|
||||||
import { TMembershipUserScopeFactory } from "../membership-user-types";
|
|
||||||
import { TMembershipUserDALFactory } from "../membership-user-dal";
|
import { TMembershipUserDALFactory } from "../membership-user-dal";
|
||||||
|
import { TMembershipUserScopeFactory } from "../membership-user-types";
|
||||||
|
|
||||||
type TOrgMembershipUserScopeFactoryDep = {
|
type TOrgMembershipUserScopeFactoryDep = {
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
|
|||||||
@@ -705,6 +705,25 @@ export const orgDALFactory = (db: TDbClient) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const findRootOrgDetails = async (orgId: string): Promise<TOrganizations | undefined> => {
|
||||||
|
try {
|
||||||
|
const org = await db
|
||||||
|
.replicaNode()(TableName.Organization)
|
||||||
|
.select(selectAllTableCols(TableName.Organization))
|
||||||
|
.where(
|
||||||
|
"id",
|
||||||
|
db(TableName.Organization)
|
||||||
|
.select(db.raw(`CASE WHEN "rootOrgId" IS NULL THEN id ELSE "rootOrgId" END`))
|
||||||
|
.where("id", orgId)
|
||||||
|
)
|
||||||
|
.first();
|
||||||
|
|
||||||
|
return org;
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "FindRootOrgDetails" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return withTransaction(db, {
|
return withTransaction(db, {
|
||||||
...orgOrm,
|
...orgOrm,
|
||||||
findOrgByProjectId,
|
findOrgByProjectId,
|
||||||
@@ -728,6 +747,7 @@ export const orgDALFactory = (db: TDbClient) => {
|
|||||||
deleteMembershipById,
|
deleteMembershipById,
|
||||||
deleteMembershipsById,
|
deleteMembershipsById,
|
||||||
updateMembership,
|
updateMembership,
|
||||||
findIdentityOrganization
|
findIdentityOrganization,
|
||||||
|
findRootOrgDetails
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { logger } from "@app/lib/logger";
|
|||||||
|
|
||||||
import { TAccessTokenQueueServiceFactory } from "../access-token-queue/access-token-queue";
|
import { TAccessTokenQueueServiceFactory } from "../access-token-queue/access-token-queue";
|
||||||
import { ActorType } from "../auth/auth-type";
|
import { ActorType } from "../auth/auth-type";
|
||||||
|
import { TOrgDALFactory } from "../org/org-dal";
|
||||||
import { TProjectDALFactory } from "../project/project-dal";
|
import { TProjectDALFactory } from "../project/project-dal";
|
||||||
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
||||||
import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
|
import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
|
||||||
@@ -25,7 +26,6 @@ import {
|
|||||||
TGetServiceTokenInfoDTO,
|
TGetServiceTokenInfoDTO,
|
||||||
TProjectServiceTokensDTO
|
TProjectServiceTokensDTO
|
||||||
} from "./service-token-types";
|
} from "./service-token-types";
|
||||||
import { TOrgDALFactory } from "../org/org-dal";
|
|
||||||
|
|
||||||
type TServiceTokenServiceFactoryDep = {
|
type TServiceTokenServiceFactoryDep = {
|
||||||
serviceTokenDAL: TServiceTokenDALFactory;
|
serviceTokenDAL: TServiceTokenDALFactory;
|
||||||
|
|||||||
@@ -1,2 +1,6 @@
|
|||||||
export { useCreateOrgIdentityMembership, useDeleteOrgIdentityMembership } from "./mutation";
|
export { useCreateOrgIdentityMembership, useDeleteOrgIdentityMembership } from "./mutation";
|
||||||
export type { TCreateOrgIdentityMembershipDTO, TDeleteOrgIdentityMembershipDTO, TOrgIdentityMembership } from "./types";
|
export type {
|
||||||
|
TCreateOrgIdentityMembershipDTO,
|
||||||
|
TDeleteOrgIdentityMembershipDTO,
|
||||||
|
TOrgIdentityMembership
|
||||||
|
} from "./types";
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
export {
|
export {
|
||||||
useAddOrgPmtMethod,
|
useAddOrgPmtMethod,
|
||||||
useGetAvailableOrgIdentities,
|
|
||||||
useAddOrgTaxId,
|
useAddOrgTaxId,
|
||||||
useCreateCustomerPortalSession,
|
useCreateCustomerPortalSession,
|
||||||
useCreateOrg,
|
useCreateOrg,
|
||||||
useDeleteOrgById,
|
useDeleteOrgById,
|
||||||
useDeleteOrgPmtMethod,
|
useDeleteOrgPmtMethod,
|
||||||
useDeleteOrgTaxId,
|
useDeleteOrgTaxId,
|
||||||
|
useGetAvailableOrgIdentities,
|
||||||
useGetIdentityMembershipOrgs,
|
useGetIdentityMembershipOrgs,
|
||||||
useGetOrganizationGroups,
|
useGetOrganizationGroups,
|
||||||
useGetOrganizations,
|
useGetOrganizations,
|
||||||
|
|||||||
@@ -582,7 +582,7 @@ export const useGetAvailableOrgIdentities = (enabled = true) =>
|
|||||||
queryKey: organizationKeys.getAvailableIdentities(),
|
queryKey: organizationKeys.getAvailableIdentities(),
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const { data } = await apiRequest.get<{ identities: { name: string; id: string }[] }>(
|
const { data } = await apiRequest.get<{ identities: { name: string; id: string }[] }>(
|
||||||
`/api/v1/organization/identities/available`
|
"/api/v1/organization/identities/available"
|
||||||
);
|
);
|
||||||
|
|
||||||
return data.identities;
|
return data.identities;
|
||||||
@@ -596,7 +596,7 @@ export const useGetAvailableOrgUsers = (enabled = true) =>
|
|||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const { data } = await apiRequest.get<{
|
const { data } = await apiRequest.get<{
|
||||||
users: { username: string; id: string; firstName: string; lastName: string }[];
|
users: { username: string; id: string; firstName: string; lastName: string }[];
|
||||||
}>(`/api/v1/organization/users/available`);
|
}>("/api/v1/organization/users/available");
|
||||||
|
|
||||||
return data.users;
|
return data.users;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -58,8 +58,8 @@ import { AuthMethod } from "@app/hooks/api/users/types";
|
|||||||
import { navigateUserToOrg } from "@app/pages/auth/LoginPage/Login.utils";
|
import { navigateUserToOrg } from "@app/pages/auth/LoginPage/Login.utils";
|
||||||
|
|
||||||
import { ServerAdminsPanel } from "../ServerAdminsPanel/ServerAdminsPanel";
|
import { ServerAdminsPanel } from "../ServerAdminsPanel/ServerAdminsPanel";
|
||||||
import { NotificationDropdown } from "./NotificationDropdown";
|
|
||||||
import { NewSubOrganizationForm } from "./NewSubOrganizationForm";
|
import { NewSubOrganizationForm } from "./NewSubOrganizationForm";
|
||||||
|
import { NotificationDropdown } from "./NotificationDropdown";
|
||||||
|
|
||||||
const getPlan = (subscription: SubscriptionPlan) => {
|
const getPlan = (subscription: SubscriptionPlan) => {
|
||||||
if (subscription.groups) return "Enterprise";
|
if (subscription.groups) return "Enterprise";
|
||||||
|
|||||||
+1
-1
@@ -4,8 +4,8 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, FormControl, Input } from "@app/components/v2";
|
import { Button, FormControl, Input } from "@app/components/v2";
|
||||||
import { GenericResourceNameSchema } from "@app/lib/schemas";
|
|
||||||
import { useCreateSubOrganization } from "@app/hooks/api";
|
import { useCreateSubOrganization } from "@app/hooks/api";
|
||||||
|
import { GenericResourceNameSchema } from "@app/lib/schemas";
|
||||||
|
|
||||||
type ContentProps = {
|
type ContentProps = {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
|
|||||||
+1
-1
@@ -105,7 +105,7 @@ export const IdentityLinkForm = ({ onClose }: Props) => {
|
|||||||
placeholder="Select role..."
|
placeholder="Select role..."
|
||||||
getOptionValue={(option) => option.slug}
|
getOptionValue={(option) => option.slug}
|
||||||
getOptionLabel={(option) => option.name}
|
getOptionLabel={(option) => option.name}
|
||||||
menuPortalTarget={document.body}
|
// menuPortalTarget={document.body}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
|
|||||||
+1
-1
@@ -53,7 +53,7 @@ export const IdentityModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
|
|
||||||
const { data: roles } = useGetOrgRoles(orgId);
|
const { data: roles } = useGetOrgRoles(orgId);
|
||||||
const isOrgIdentity = orgId === popUp?.identity?.data?.orgId;
|
const isOrgIdentity = popUp?.identity?.data ? orgId === popUp?.identity?.data?.orgId : true;
|
||||||
|
|
||||||
const { mutateAsync: createMutateAsync } = useCreateIdentity();
|
const { mutateAsync: createMutateAsync } = useCreateIdentity();
|
||||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentity();
|
const { mutateAsync: updateMutateAsync } = useUpdateIdentity();
|
||||||
|
|||||||
+1
-1
@@ -24,11 +24,11 @@ import { usePopUp } from "@app/hooks/usePopUp";
|
|||||||
|
|
||||||
import { IdentityAuthTemplateModal } from "./IdentityAuthTemplateModal";
|
import { IdentityAuthTemplateModal } from "./IdentityAuthTemplateModal";
|
||||||
import { IdentityAuthTemplatesTable } from "./IdentityAuthTemplatesTable";
|
import { IdentityAuthTemplatesTable } from "./IdentityAuthTemplatesTable";
|
||||||
|
import { IdentityLinkForm } from "./IdentityLinkForm";
|
||||||
import { IdentityModal } from "./IdentityModal";
|
import { IdentityModal } from "./IdentityModal";
|
||||||
import { IdentityTable } from "./IdentityTable";
|
import { IdentityTable } from "./IdentityTable";
|
||||||
import { IdentityTokenAuthTokenModal } from "./IdentityTokenAuthTokenModal";
|
import { IdentityTokenAuthTokenModal } from "./IdentityTokenAuthTokenModal";
|
||||||
import { MachineAuthTemplateUsagesModal } from "./MachineAuthTemplateUsagesModal";
|
import { MachineAuthTemplateUsagesModal } from "./MachineAuthTemplateUsagesModal";
|
||||||
import { IdentityLinkForm } from "./IdentityLinkForm";
|
|
||||||
|
|
||||||
export const IdentitySection = withPermission(
|
export const IdentitySection = withPermission(
|
||||||
() => {
|
() => {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useSearch } from "@tanstack/react-router";
|
|||||||
|
|
||||||
import { Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
|
import { Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
|
||||||
import { ROUTE_PATHS } from "@app/const/routes";
|
import { ROUTE_PATHS } from "@app/const/routes";
|
||||||
|
import { useOrganization } from "@app/context";
|
||||||
|
|
||||||
import { AuditLogStreamsTab } from "../AuditLogStreamTab";
|
import { AuditLogStreamsTab } from "../AuditLogStreamTab";
|
||||||
import { ExternalMigrationsTab } from "../ExternalMigrationsTab";
|
import { ExternalMigrationsTab } from "../ExternalMigrationsTab";
|
||||||
@@ -14,7 +15,6 @@ import { OrgSecurityTab } from "../OrgSecurityTab";
|
|||||||
import { OrgSsoTab } from "../OrgSsoTab";
|
import { OrgSsoTab } from "../OrgSsoTab";
|
||||||
import { OrgWorkflowIntegrationTab } from "../OrgWorkflowIntegrationTab";
|
import { OrgWorkflowIntegrationTab } from "../OrgWorkflowIntegrationTab";
|
||||||
import { ProjectTemplatesTab } from "../ProjectTemplatesTab";
|
import { ProjectTemplatesTab } from "../ProjectTemplatesTab";
|
||||||
import { useOrganization } from "@app/context";
|
|
||||||
|
|
||||||
export const OrgTabGroup = () => {
|
export const OrgTabGroup = () => {
|
||||||
const search = useSearch({
|
const search = useSearch({
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createFileRoute, retainSearchParams } from "@tanstack/react-router";
|
import { createFileRoute, retainSearchParams } from "@tanstack/react-router";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
import { OrganizationLayout } from "@app/layouts/OrganizationLayout";
|
import { OrganizationLayout } from "@app/layouts/OrganizationLayout";
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/_authenticate/_inject-org-details/_org-layout")({
|
export const Route = createFileRoute("/_authenticate/_inject-org-details/_org-layout")({
|
||||||
component: OrganizationLayout,
|
component: OrganizationLayout,
|
||||||
|
|||||||
Reference in New Issue
Block a user