|
4 | 4 | * Get organization seat analytics including member activity. |
5 | 5 | * |
6 | 6 | * Response: AdminSingleResponse<AdminSeatAnalytics> |
7 | | - * |
8 | | - * PATCH /api/v1/admin/organizations/[id]/seats |
9 | | - * |
10 | | - * Update organization seat count with Stripe sync (matches user flow). |
11 | | - * |
12 | | - * Body: |
13 | | - * - seats: number - New seat count (positive integer) |
14 | | - * |
15 | | - * Response: AdminSingleResponse<{ success: true, seats: number, plan: string, stripeUpdated?: boolean }> |
16 | 7 | */ |
17 | 8 |
|
18 | | -import { db } from '@sim/db' |
19 | | -import { organization, subscription } from '@sim/db/schema' |
20 | | -import { and, eq } from 'drizzle-orm' |
21 | | -import { requireStripeClient } from '@/lib/billing/stripe-client' |
22 | 9 | import { getOrganizationSeatAnalytics } from '@/lib/billing/validation/seat-management' |
23 | 10 | import { createLogger } from '@/lib/logs/console/logger' |
24 | 11 | import { withAdminAuthParams } from '@/app/api/v1/admin/middleware' |
25 | 12 | import { |
26 | | - badRequestResponse, |
27 | 13 | internalErrorResponse, |
28 | 14 | notFoundResponse, |
29 | 15 | singleResponse, |
@@ -75,122 +61,3 @@ export const GET = withAdminAuthParams<RouteParams>(async (_, context) => { |
75 | 61 | return internalErrorResponse('Failed to get organization seats') |
76 | 62 | } |
77 | 63 | }) |
78 | | - |
79 | | -export const PATCH = withAdminAuthParams<RouteParams>(async (request, context) => { |
80 | | - const { id: organizationId } = await context.params |
81 | | - |
82 | | - try { |
83 | | - const body = await request.json() |
84 | | - |
85 | | - if (typeof body.seats !== 'number' || body.seats < 1 || !Number.isInteger(body.seats)) { |
86 | | - return badRequestResponse('seats must be a positive integer') |
87 | | - } |
88 | | - |
89 | | - const [orgData] = await db |
90 | | - .select({ id: organization.id }) |
91 | | - .from(organization) |
92 | | - .where(eq(organization.id, organizationId)) |
93 | | - .limit(1) |
94 | | - |
95 | | - if (!orgData) { |
96 | | - return notFoundResponse('Organization') |
97 | | - } |
98 | | - |
99 | | - const [subData] = await db |
100 | | - .select() |
101 | | - .from(subscription) |
102 | | - .where(and(eq(subscription.referenceId, organizationId), eq(subscription.status, 'active'))) |
103 | | - .limit(1) |
104 | | - |
105 | | - if (!subData) { |
106 | | - return notFoundResponse('Subscription') |
107 | | - } |
108 | | - |
109 | | - const newSeatCount = body.seats |
110 | | - let stripeUpdated = false |
111 | | - |
112 | | - if (subData.plan === 'enterprise') { |
113 | | - const currentMetadata = (subData.metadata as Record<string, unknown>) || {} |
114 | | - const newMetadata = { |
115 | | - ...currentMetadata, |
116 | | - seats: newSeatCount, |
117 | | - } |
118 | | - |
119 | | - await db |
120 | | - .update(subscription) |
121 | | - .set({ metadata: newMetadata }) |
122 | | - .where(eq(subscription.id, subData.id)) |
123 | | - |
124 | | - logger.info(`Admin API: Updated enterprise seats for organization ${organizationId}`, { |
125 | | - seats: newSeatCount, |
126 | | - }) |
127 | | - } else if (subData.plan === 'team') { |
128 | | - if (subData.stripeSubscriptionId) { |
129 | | - const stripe = requireStripeClient() |
130 | | - |
131 | | - const stripeSubscription = await stripe.subscriptions.retrieve(subData.stripeSubscriptionId) |
132 | | - |
133 | | - if (stripeSubscription.status !== 'active') { |
134 | | - return badRequestResponse('Stripe subscription is not active') |
135 | | - } |
136 | | - |
137 | | - const subscriptionItem = stripeSubscription.items.data[0] |
138 | | - if (!subscriptionItem) { |
139 | | - return internalErrorResponse('No subscription item found in Stripe subscription') |
140 | | - } |
141 | | - |
142 | | - const currentSeats = subData.seats || 1 |
143 | | - |
144 | | - logger.info('Admin API: Updating Stripe subscription quantity', { |
145 | | - organizationId, |
146 | | - stripeSubscriptionId: subData.stripeSubscriptionId, |
147 | | - subscriptionItemId: subscriptionItem.id, |
148 | | - currentSeats, |
149 | | - newSeatCount, |
150 | | - }) |
151 | | - |
152 | | - await stripe.subscriptions.update(subData.stripeSubscriptionId, { |
153 | | - items: [ |
154 | | - { |
155 | | - id: subscriptionItem.id, |
156 | | - quantity: newSeatCount, |
157 | | - }, |
158 | | - ], |
159 | | - proration_behavior: 'create_prorations', |
160 | | - }) |
161 | | - |
162 | | - stripeUpdated = true |
163 | | - } |
164 | | - |
165 | | - await db |
166 | | - .update(subscription) |
167 | | - .set({ seats: newSeatCount }) |
168 | | - .where(eq(subscription.id, subData.id)) |
169 | | - |
170 | | - logger.info(`Admin API: Updated team seats for organization ${organizationId}`, { |
171 | | - seats: newSeatCount, |
172 | | - stripeUpdated, |
173 | | - }) |
174 | | - } else { |
175 | | - await db |
176 | | - .update(subscription) |
177 | | - .set({ seats: newSeatCount }) |
178 | | - .where(eq(subscription.id, subData.id)) |
179 | | - |
180 | | - logger.info(`Admin API: Updated seats for organization ${organizationId}`, { |
181 | | - seats: newSeatCount, |
182 | | - plan: subData.plan, |
183 | | - }) |
184 | | - } |
185 | | - |
186 | | - return singleResponse({ |
187 | | - success: true, |
188 | | - seats: newSeatCount, |
189 | | - plan: subData.plan, |
190 | | - stripeUpdated, |
191 | | - }) |
192 | | - } catch (error) { |
193 | | - logger.error('Admin API: Failed to update organization seats', { error, organizationId }) |
194 | | - return internalErrorResponse('Failed to update organization seats') |
195 | | - } |
196 | | -}) |
0 commit comments