@@ -4,9 +4,10 @@ import type { NextRequest } from 'next/server'
44import { v4 as uuidv4 } from 'uuid'
55import { generateApiKey } from '@/lib/api-key/service'
66import { createLogger } from '@/lib/logs/console/logger'
7+ import { hasWorkspaceAdminAccess } from '@/lib/permissions/utils'
78import { generateRequestId } from '@/lib/utils'
89import { loadWorkflowFromNormalizedTables } from '@/lib/workflows/db-helpers'
9- import { validateWorkflowAccess } from '@/app/api/ workflows/middleware '
10+ import { validateWorkflowPermissions } from '@/lib/ workflows/utils '
1011import { createErrorResponse , createSuccessResponse } from '@/app/api/workflows/utils'
1112
1213const logger = createLogger ( 'WorkflowDeployAPI' )
@@ -20,33 +21,16 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
2021
2122 try {
2223 logger . debug ( `[${ requestId } ] Fetching deployment info for workflow: ${ id } ` )
23- const validation = await validateWorkflowAccess ( request , id , false )
2424
25- if ( validation . error ) {
26- logger . warn ( `[${ requestId } ] Failed to fetch deployment info: ${ validation . error . message } ` )
27- return createErrorResponse ( validation . error . message , validation . error . status )
25+ const { error, workflow : workflowData } = await validateWorkflowPermissions (
26+ id ,
27+ requestId ,
28+ 'read'
29+ )
30+ if ( error ) {
31+ return createErrorResponse ( error . message , error . status )
2832 }
2933
30- // Fetch the workflow information including deployment details
31- const result = await db
32- . select ( {
33- isDeployed : workflow . isDeployed ,
34- deployedAt : workflow . deployedAt ,
35- userId : workflow . userId ,
36- pinnedApiKeyId : workflow . pinnedApiKeyId ,
37- } )
38- . from ( workflow )
39- . where ( eq ( workflow . id , id ) )
40- . limit ( 1 )
41-
42- if ( result . length === 0 ) {
43- logger . warn ( `[${ requestId } ] Workflow not found: ${ id } ` )
44- return createErrorResponse ( 'Workflow not found' , 404 )
45- }
46-
47- const workflowData = result [ 0 ]
48-
49- // If the workflow is not deployed, return appropriate response
5034 if ( ! workflowData . isDeployed ) {
5135 logger . info ( `[${ requestId } ] Workflow is not deployed: ${ id } ` )
5236 return createSuccessResponse ( {
@@ -70,7 +54,6 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
7054 keyInfo = { name : pinnedKey [ 0 ] . name , type : pinnedKey [ 0 ] . type as 'personal' | 'workspace' }
7155 }
7256 } else {
73- // Fetch the user's API key, preferring the most recently used
7457 const userApiKey = await db
7558 . select ( {
7659 key : apiKey . key ,
@@ -82,7 +65,6 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
8265 . orderBy ( desc ( apiKey . lastUsed ) , desc ( apiKey . createdAt ) )
8366 . limit ( 1 )
8467
85- // If no API key exists, create one automatically
8668 if ( userApiKey . length === 0 ) {
8769 try {
8870 const newApiKeyVal = generateApiKey ( )
@@ -107,7 +89,6 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
10789 }
10890 }
10991
110- // Check if the workflow has meaningful changes that would require redeployment
11192 let needsRedeployment = false
11293 const [ active ] = await db
11394 . select ( { state : workflowDeploymentVersion . state } )
@@ -158,42 +139,26 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
158139
159140 try {
160141 logger . debug ( `[${ requestId } ] Deploying workflow: ${ id } ` )
161- const validation = await validateWorkflowAccess ( request , id , false )
162142
163- if ( validation . error ) {
164- logger . warn ( `[${ requestId } ] Workflow deployment failed: ${ validation . error . message } ` )
165- return createErrorResponse ( validation . error . message , validation . error . status )
143+ const {
144+ error,
145+ session,
146+ workflow : workflowData ,
147+ } = await validateWorkflowPermissions ( id , requestId , 'write' )
148+ if ( error ) {
149+ return createErrorResponse ( error . message , error . status )
166150 }
167151
168- // Get the workflow to find the user and existing pin (removed deprecated state column)
169- const workflowData = await db
170- . select ( {
171- userId : workflow . userId ,
172- pinnedApiKeyId : workflow . pinnedApiKeyId ,
173- } )
174- . from ( workflow )
175- . where ( eq ( workflow . id , id ) )
176- . limit ( 1 )
177-
178- if ( workflowData . length === 0 ) {
179- logger . warn ( `[${ requestId } ] Workflow not found: ${ id } ` )
180- return createErrorResponse ( 'Workflow not found' , 404 )
181- }
152+ const userId = workflowData ! . userId
182153
183- const userId = workflowData [ 0 ] . userId
184-
185- // Parse request body to capture selected API key (if provided)
186154 let providedApiKey : string | null = null
187155 try {
188156 const parsed = await request . json ( )
189157 if ( parsed && typeof parsed . apiKey === 'string' && parsed . apiKey . trim ( ) . length > 0 ) {
190158 providedApiKey = parsed . apiKey . trim ( )
191159 }
192- } catch ( _err ) {
193- // Body may be empty; ignore
194- }
160+ } catch ( _err ) { }
195161
196- // Get the current live state from normalized tables using centralized helper
197162 logger . debug ( `[${ requestId } ] Getting current workflow state for deployment` )
198163
199164 const normalizedData = await loadWorkflowFromNormalizedTables ( id )
@@ -226,7 +191,6 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
226191 const deployedAt = new Date ( )
227192 logger . debug ( `[${ requestId } ] Proceeding with deployment at ${ deployedAt . toISOString ( ) } ` )
228193
229- // Check if the user already has API keys
230194 const userApiKey = await db
231195 . select ( {
232196 key : apiKey . key ,
@@ -236,23 +200,21 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
236200 . orderBy ( desc ( apiKey . lastUsed ) , desc ( apiKey . createdAt ) )
237201 . limit ( 1 )
238202
239- // If no API key exists, create one
240203 if ( userApiKey . length === 0 ) {
241204 try {
242205 const newApiKey = generateApiKey ( )
243206 await db . insert ( apiKey ) . values ( {
244207 id : uuidv4 ( ) ,
245208 userId,
246- workspaceId : null , // Personal keys must have NULL workspaceId
209+ workspaceId : null ,
247210 name : 'Default API Key' ,
248211 key : newApiKey ,
249- type : 'personal' , // Explicitly set type
212+ type : 'personal' ,
250213 createdAt : new Date ( ) ,
251214 updatedAt : new Date ( ) ,
252215 } )
253216 logger . info ( `[${ requestId } ] Generated new API key for user: ${ userId } ` )
254217 } catch ( keyError ) {
255- // If key generation fails, log the error but continue with the request
256218 logger . error ( `[${ requestId } ] Failed to generate API key:` , keyError )
257219 }
258220 }
@@ -268,30 +230,49 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
268230 if ( providedApiKey ) {
269231 let isValidKey = false
270232
271- const [ personalKey ] = await db
272- . select ( { id : apiKey . id , key : apiKey . key , name : apiKey . name , expiresAt : apiKey . expiresAt } )
273- . from ( apiKey )
274- . where (
275- and ( eq ( apiKey . id , providedApiKey ) , eq ( apiKey . userId , userId ) , eq ( apiKey . type , 'personal' ) )
276- )
277- . limit ( 1 )
233+ const currentUserId = session ?. user ?. id
278234
279- if ( personalKey ) {
280- if ( ! personalKey . expiresAt || personalKey . expiresAt >= new Date ( ) ) {
281- matchedKey = { ...personalKey , type : 'personal' }
282- isValidKey = true
283- keyInfo = { name : personalKey . name , type : 'personal' }
284- }
285- }
286-
287- if ( ! isValidKey ) {
235+ if ( currentUserId ) {
288236 const [ workflowData ] = await db
289237 . select ( { workspaceId : workflow . workspaceId } )
290238 . from ( workflow )
291239 . where ( eq ( workflow . id , id ) )
292240 . limit ( 1 )
293241
294242 if ( workflowData ?. workspaceId ) {
243+ const isAdmin = await hasWorkspaceAdminAccess ( currentUserId , workflowData . workspaceId )
244+
245+ if ( isAdmin ) {
246+ const [ personalKey ] = await db
247+ . select ( {
248+ id : apiKey . id ,
249+ key : apiKey . key ,
250+ name : apiKey . name ,
251+ expiresAt : apiKey . expiresAt ,
252+ } )
253+ . from ( apiKey )
254+ . where (
255+ and (
256+ eq ( apiKey . id , providedApiKey ) ,
257+ eq ( apiKey . userId , currentUserId ) ,
258+ eq ( apiKey . type , 'personal' )
259+ )
260+ )
261+ . limit ( 1 )
262+
263+ if ( personalKey ) {
264+ if ( ! personalKey . expiresAt || personalKey . expiresAt >= new Date ( ) ) {
265+ matchedKey = { ...personalKey , type : 'personal' }
266+ isValidKey = true
267+ keyInfo = { name : personalKey . name , type : 'personal' }
268+ }
269+ }
270+ }
271+ }
272+ }
273+
274+ if ( ! isValidKey ) {
275+ if ( workflowData ! . workspaceId ) {
295276 const [ workspaceKey ] = await db
296277 . select ( {
297278 id : apiKey . id ,
@@ -303,7 +284,7 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
303284 . where (
304285 and (
305286 eq ( apiKey . id , providedApiKey ) ,
306- eq ( apiKey . workspaceId , workflowData . workspaceId ) ,
287+ eq ( apiKey . workspaceId , workflowData ! . workspaceId ) ,
307288 eq ( apiKey . type , 'workspace' )
308289 )
309290 )
@@ -325,7 +306,6 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
325306 }
326307 }
327308
328- // In a transaction: create deployment version, update workflow flags and deployed state
329309 await db . transaction ( async ( tx ) => {
330310 const [ { maxVersion } ] = await tx
331311 . select ( { maxVersion : sql `COALESCE(MAX("version"), 0)` } )
@@ -366,7 +346,6 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
366346 await tx . update ( workflow ) . set ( updateData ) . where ( eq ( workflow . id , id ) )
367347 } )
368348
369- // Update lastUsed for the key we returned
370349 if ( matchedKey ) {
371350 try {
372351 await db
@@ -408,14 +387,12 @@ export async function DELETE(
408387
409388 try {
410389 logger . debug ( `[${ requestId } ] Undeploying workflow: ${ id } ` )
411- const validation = await validateWorkflowAccess ( request , id , false )
412390
413- if ( validation . error ) {
414- logger . warn ( `[ ${ requestId } ] Workflow undeployment failed: ${ validation . error . message } ` )
415- return createErrorResponse ( validation . error . message , validation . error . status )
391+ const { error } = await validateWorkflowPermissions ( id , requestId , 'write' )
392+ if ( error ) {
393+ return createErrorResponse ( error . message , error . status )
416394 }
417395
418- // Deactivate versions and clear deployment fields
419396 await db . transaction ( async ( tx ) => {
420397 await tx
421398 . update ( workflowDeploymentVersion )
0 commit comments