Skip to content

Commit c1e39ac

Browse files
committed
address more comments
1 parent 2a4a417 commit c1e39ac

2 files changed

Lines changed: 24 additions & 7 deletions

File tree

apps/sim/app/api/organizations/[id]/invitations/[invitationId]/route.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,10 +422,20 @@ export async function PUT(
422422
// this transfer, pre-join bytes would be orphaned on the
423423
// user's row and subsequent decrements (deleting a pre-join
424424
// file after joining) would wrongly reduce the org pool.
425+
//
426+
// `.for('update')` acquires a row-level write lock on the
427+
// user's `user_stats` row so a concurrent
428+
// `incrementStorageUsage`/`decrementStorageUsage` (from
429+
// another tab, a scheduled run, an API-key writer, etc.)
430+
// blocks until this transaction commits — otherwise Postgres
431+
// READ COMMITTED would let a write land between the snapshot
432+
// SELECT and the zero UPDATE, silently dropping those bytes.
433+
// Mirrors the bulk version in `syncSubscriptionUsageLimits`.
425434
const storageRows = await tx
426435
.select({ storageUsedBytes: userStats.storageUsedBytes })
427436
.from(userStats)
428437
.where(eq(userStats.userId, userId))
438+
.for('update')
429439
.limit(1)
430440

431441
const bytesToTransfer = storageRows[0]?.storageUsedBytes ?? 0

apps/sim/app/workspace/[workspaceId]/settings/components/subscription/subscription-permissions.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,14 @@ export function getSubscriptionPermissions(
5858
canEditUsageLimit: (isFree || (isPaid && !isEnterprise)) && orgAdminOrSolo,
5959
canCancelSubscription: isPaid && !isEnterprise && orgAdminOrSolo,
6060
showTeamMemberView: orgMemberOnly,
61+
// Personal Pro can upgrade to team/enterprise. Any org admin/owner on
62+
// a non-enterprise plan can upgrade to enterprise — covers team admins
63+
// AND admins of `pro_*` plans attached to an org (previously missed by
64+
// the narrower `isTeam && isTeamAdmin` check, which left pro-on-org
65+
// admins with no upgrade path even though `getVisiblePlans` listed
66+
// enterprise for them).
6167
showUpgradePlans:
62-
(isFree || (isPro && !isOrgScoped) || (isTeam && isTeamAdmin)) && !isEnterprise,
68+
(isFree || (isPro && !isOrgScoped) || (isOrgScoped && isTeamAdmin)) && !isEnterprise,
6369
isEnterpriseMember,
6470
canViewUsageInfo,
6571
}
@@ -70,7 +76,7 @@ export function getVisiblePlans(
7076
userRole: UserRole
7177
): ('pro' | 'team' | 'enterprise')[] {
7278
const plans: ('pro' | 'team' | 'enterprise')[] = []
73-
const { isFree, isPro, isTeam, isOrgScoped } = subscription
79+
const { isFree, isPro, isEnterprise, isOrgScoped } = subscription
7480
const { isTeamAdmin } = userRole
7581

7682
// Free users see all plans
@@ -81,13 +87,14 @@ export function getVisiblePlans(
8187
else if (isPro && !isOrgScoped) {
8288
plans.push('team', 'enterprise')
8389
}
84-
// Team/org owners/admins: only enterprise (already on a team-level plan)
85-
else if (isOrgScoped && isTeamAdmin && !isTeam) {
86-
plans.push('enterprise')
87-
} else if (isTeam && isTeamAdmin) {
90+
// Org admin/owner on a non-enterprise plan: enterprise is the only
91+
// remaining upgrade. Covers team admins and `pro_*`-on-org admins.
92+
// Explicitly excludes enterprise admins (already on the top tier) so
93+
// this stays consistent with `showUpgradePlans`.
94+
else if (isOrgScoped && isTeamAdmin && !isEnterprise) {
8895
plans.push('enterprise')
8996
}
90-
// Team/org members, Enterprise users see no plans
97+
// Org members, Enterprise users see no plans
9198

9299
return plans
93100
}

0 commit comments

Comments
 (0)