Просмотр исходного кода

limit the number of orgs created (#28)

Bernt Christian Egeland 7 месяцев назад
Родитель
Сommit
28ecffbf29
2 измененных файлов с 43 добавлено и 0 удалено
  1. 14 0
      src/features/team/Actions/createNewOrganization.ts
  2. 29 0
      src/lib/features.ts

+ 14 - 0
src/features/team/Actions/createNewOrganization.ts

@@ -3,6 +3,7 @@
 import { cookies } from "next/headers";
 import { cookies } from "next/headers";
 import { db } from "@/lib/db";
 import { db } from "@/lib/db";
 import { withAuth } from "@/lib/with-auth";
 import { withAuth } from "@/lib/with-auth";
+import { isCloudMode, getMaxOrganizations } from "@/lib/features";
 import { createOrganizationSchema } from "../Schema/teamSchema";
 import { createOrganizationSchema } from "../Schema/teamSchema";
 import { revalidatePath } from "next/cache";
 import { revalidatePath } from "next/cache";
 
 
@@ -10,6 +11,19 @@ export async function createNewOrganization(input: unknown) {
   return withAuth(async ({ userId }) => {
   return withAuth(async ({ userId }) => {
     const data = createOrganizationSchema.parse(input);
     const data = createOrganizationSchema.parse(input);
 
 
+    if (isCloudMode()) {
+      const ownedCount = await db.organizationMember.count({
+        where: { userId, role: "owner" },
+      });
+      const maxOrgs = await getMaxOrganizations(userId);
+
+      if (ownedCount >= maxOrgs) {
+        throw new Error(
+          `You have reached the maximum number of organizations (${maxOrgs}) for your plan. Upgrade to create more.`,
+        );
+      }
+    }
+
     const org = await db.$transaction(async (tx) => {
     const org = await db.$transaction(async (tx) => {
       const created = await tx.organization.create({
       const created = await tx.organization.create({
         data: { name: data.name },
         data: { name: data.name },

+ 29 - 0
src/lib/features.ts

@@ -4,6 +4,7 @@ import { db } from './db'
 export type Plan = 'free' | 'pro' | 'enterprise' | 'white-label'
 export type Plan = 'free' | 'pro' | 'enterprise' | 'white-label'
 
 
 export type PlanFeatures = {
 export type PlanFeatures = {
+  maxOrganizations: number
   maxCustomers: number
   maxCustomers: number
   maxUsers: number
   maxUsers: number
   templates: number
   templates: number
@@ -24,6 +25,7 @@ export type PlanFeatures = {
 
 
 export const PLAN_FEATURES: Record<Plan, PlanFeatures> = {
 export const PLAN_FEATURES: Record<Plan, PlanFeatures> = {
   free: {
   free: {
+    maxOrganizations: 1,
     maxCustomers: 5,
     maxCustomers: 5,
     maxUsers: 1,
     maxUsers: 1,
     templates: 2,
     templates: 2,
@@ -42,6 +44,7 @@ export const PLAN_FEATURES: Record<Plan, PlanFeatures> = {
     customerPortal: false,
     customerPortal: false,
   },
   },
   pro: {
   pro: {
+    maxOrganizations: 3,
     maxCustomers: 999999,
     maxCustomers: 999999,
     maxUsers: 5,
     maxUsers: 5,
     templates: 999999,
     templates: 999999,
@@ -60,6 +63,7 @@ export const PLAN_FEATURES: Record<Plan, PlanFeatures> = {
     customerPortal: true,
     customerPortal: true,
   },
   },
   enterprise: {
   enterprise: {
+    maxOrganizations: 10,
     maxCustomers: 999999,
     maxCustomers: 999999,
     maxUsers: 50,
     maxUsers: 50,
     templates: 999999,
     templates: 999999,
@@ -78,6 +82,7 @@ export const PLAN_FEATURES: Record<Plan, PlanFeatures> = {
     customerPortal: true,
     customerPortal: true,
   },
   },
   'white-label': {
   'white-label': {
+    maxOrganizations: 999999,
     maxCustomers: 999999,
     maxCustomers: 999999,
     maxUsers: 999999,
     maxUsers: 999999,
     templates: 999999,
     templates: 999999,
@@ -156,6 +161,30 @@ export const getFeatures = cache(async (organizationId: string): Promise<PlanFea
   }
   }
 })
 })
 
 
+/**
+ * Returns the max organizations a user is allowed based on their best plan
+ * across all orgs they own. In self-hosted mode, returns unlimited.
+ */
+export async function getMaxOrganizations(userId: string): Promise<number> {
+  if (!isCloudMode()) return 999999
+
+  const ownedOrgs = await db.organizationMember.findMany({
+    where: { userId, role: 'owner' },
+    select: { organizationId: true },
+  })
+
+  let best = PLAN_FEATURES.free.maxOrganizations
+
+  for (const membership of ownedOrgs) {
+    const features = await getFeatures(membership.organizationId)
+    if (features.maxOrganizations > best) {
+      best = features.maxOrganizations
+    }
+  }
+
+  return best
+}
+
 export class FeatureGatedError extends Error {
 export class FeatureGatedError extends Error {
   feature: string
   feature: string