-
Notifications
You must be signed in to change notification settings - Fork 3.6k
feat(workspaces): bypass personal workspace limit for platform admins #4463
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: staging
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| import { db } from '@sim/db' | ||
| import { member, type WorkspaceMode, workspace } from '@sim/db/schema' | ||
| import { member, user, type WorkspaceMode, workspace } from '@sim/db/schema' | ||
| import { createLogger } from '@sim/logger' | ||
| import { and, count, eq, isNull } from 'drizzle-orm' | ||
| import { getOrganizationSubscription } from '@/lib/billing/core/billing' | ||
|
|
@@ -276,6 +276,32 @@ export async function getWorkspaceCreationPolicy({ | |
| } | ||
| } | ||
|
|
||
| if (await isPlatformAdmin(userId)) { | ||
| if (organizationId && orgRole && ['owner', 'admin'].includes(orgRole)) { | ||
| return { | ||
| canCreate: true, | ||
| workspaceMode: WORKSPACE_MODE.ORGANIZATION, | ||
| organizationId, | ||
| billedAccountUserId: await requireOrganizationOwnerId(organizationId), | ||
| maxWorkspaces: null, | ||
| currentWorkspaceCount: 0, | ||
| reason: null, | ||
| status: 200, | ||
| } | ||
| } | ||
|
|
||
| return { | ||
| canCreate: true, | ||
| workspaceMode: WORKSPACE_MODE.PERSONAL, | ||
| organizationId: null, | ||
| billedAccountUserId: userId, | ||
| maxWorkspaces: null, | ||
| currentWorkspaceCount: await countNonOrganizationOwnedWorkspaces(userId), | ||
| reason: null, | ||
| status: 200, | ||
| } | ||
| } | ||
|
Comment on lines
+279
to
+303
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a platform admin calls |
||
|
|
||
| const highestPrioritySubscription = await getHighestPrioritySubscription(userId) | ||
| const plan = highestPrioritySubscription?.plan | ||
| const maxWorkspaces = isMax(plan) ? 10 : isPro(plan) ? 3 : 1 | ||
|
|
@@ -331,6 +357,12 @@ export async function getOrganizationOwnerId(organizationId: string): Promise<st | |
| return ownerMembership?.userId ?? null | ||
| } | ||
|
|
||
| async function isPlatformAdmin(userId: string): Promise<boolean> { | ||
| const [row] = await db.select({ role: user.role }).from(user).where(eq(user.id, userId)).limit(1) | ||
|
|
||
| return row?.role === 'admin' | ||
| } | ||
|
|
||
| /** | ||
| * Like `getOrganizationOwnerId` but throws when no owner row exists. | ||
| * Use when the caller needs a guaranteed billed-account userId — every | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isPlatformAdminissues a fullSELECTround-trip for every call that reaches that branch — i.e., every billing-enabled personal workspace policy check where the user is not in an active org with a team/enterprise subscription. Since admins are rare, the vast majority of these queries will returnrole: 'user'and the result is discarded immediately before the subscription lookup begins. The admin bypass could be guarded with an early-exit check from a value already in scope (e.g. a cached session attribute or a flag passed in by the caller), avoiding the extra query for the common case.