1+ import { NextRequest , NextResponse } from 'next/server'
2+ import { createServerClient } from '@/lib/supabase-server'
3+ import {
4+ Task ,
5+ CreateTaskData ,
6+ createTask ,
7+ getAllTasks ,
8+ deleteTasksByStatus ,
9+ createTaskLogger ,
10+ } from '@/lib/tasks'
11+
12+ export const maxDuration = 300 // 5 minutes timeout
13+ export const runtime = 'nodejs'
14+
15+ export async function GET ( ) {
16+ try {
17+ const supabase = createServerClient ( )
18+ const tasks = await getAllTasks ( supabase )
19+ return NextResponse . json ( { tasks } )
20+ } catch ( error ) {
21+ console . error ( 'Error fetching tasks:' , error )
22+ return NextResponse . json ( { error : 'Failed to fetch tasks' } , { status : 500 } )
23+ }
24+ }
25+
26+ export async function POST ( request : NextRequest ) {
27+ try {
28+ const supabase = createServerClient ( )
29+ const body = await request . json ( )
30+
31+ // Get user from Supabase auth
32+ const { data : { user } } = await supabase . auth . getUser ( )
33+
34+ const taskData : CreateTaskData = {
35+ prompt : body . prompt ,
36+ repo_url : body . repoUrl ,
37+ selected_agent : body . selectedAgent || 'claude' ,
38+ selected_model : body . selectedModel ,
39+ user_id : user ?. id ,
40+ }
41+
42+ // Validate required fields
43+ if ( ! taskData . prompt ) {
44+ return NextResponse . json ( { error : 'Prompt is required' } , { status : 400 } )
45+ }
46+
47+ const newTask = await createTask ( supabase , taskData )
48+
49+ if ( ! newTask ) {
50+ return NextResponse . json ( { error : 'Failed to create task' } , { status : 500 } )
51+ }
52+
53+ // Process the task asynchronously with timeout
54+ processTaskWithTimeout ( newTask . id , taskData )
55+
56+ return NextResponse . json ( { task : newTask } )
57+ } catch ( error ) {
58+ console . error ( 'Error creating task:' , error )
59+ return NextResponse . json ( { error : 'Failed to create task' } , { status : 500 } )
60+ }
61+ }
62+
63+ async function processTaskWithTimeout ( taskId : string , taskData : CreateTaskData ) {
64+ const TASK_TIMEOUT_MS = 5 * 60 * 1000 // 5 minutes in milliseconds
65+
66+ // Add a warning at 4 minutes
67+ const warningTimeout = setTimeout ( async ( ) => {
68+ try {
69+ const supabase = createServerClient ( )
70+ const logger = createTaskLogger ( supabase , taskId )
71+ await logger . info ( 'Task is taking longer than expected (4+ minutes). Will timeout in 1 minute.' )
72+ } catch ( error ) {
73+ console . error ( 'Failed to add timeout warning:' , error )
74+ }
75+ } , 4 * 60 * 1000 ) // 4 minutes
76+
77+ const timeoutPromise = new Promise < never > ( ( _ , reject ) => {
78+ setTimeout ( ( ) => {
79+ reject ( new Error ( 'Task execution timed out after 5 minutes' ) )
80+ } , TASK_TIMEOUT_MS )
81+ } )
82+
83+ try {
84+ await Promise . race ( [
85+ processTask ( taskId , taskData ) ,
86+ timeoutPromise
87+ ] )
88+
89+ // Clear the warning timeout if task completes successfully
90+ clearTimeout ( warningTimeout )
91+ } catch ( error : any ) {
92+ // Clear the warning timeout on any error
93+ clearTimeout ( warningTimeout )
94+
95+ // Handle timeout specifically
96+ if ( error . message ?. includes ( 'timed out after 5 minutes' ) ) {
97+ console . error ( 'Task timed out:' , taskId )
98+
99+ const supabase = createServerClient ( )
100+ const logger = createTaskLogger ( supabase , taskId )
101+ await logger . error ( 'Task execution timed out after 5 minutes' )
102+ await logger . updateStatus ( 'error' , 'Task execution timed out after 5 minutes. The operation took too long to complete.' )
103+ } else {
104+ // Re-throw other errors to be handled by the original error handler
105+ throw error
106+ }
107+ }
108+ }
109+
110+ async function processTask ( taskId : string , taskData : CreateTaskData ) {
111+ const supabase = createServerClient ( )
112+ const logger = createTaskLogger ( supabase , taskId )
113+
114+ try {
115+ // Update task status to processing with real-time logging
116+ await logger . updateStatus ( 'processing' , 'Task created, preparing to start...' )
117+ await logger . updateProgress ( 10 , 'Initializing task execution...' )
118+
119+ // Simulate task processing (replace with actual implementation)
120+ await logger . updateProgress ( 25 , 'Setting up environment...' )
121+
122+ // Simulate some work
123+ await new Promise ( resolve => setTimeout ( resolve , 2000 ) )
124+
125+ await logger . updateProgress ( 50 , 'Processing request...' )
126+
127+ // For now, we'll simulate the task completion
128+ // In a real implementation, you would:
129+ // 1. Create sandbox environment
130+ // 2. Execute the selected agent
131+ // 3. Process the results
132+ // 4. Handle git operations
133+
134+ await logger . success ( 'Task processing completed successfully' )
135+
136+ // Simulate more work
137+ await new Promise ( resolve => setTimeout ( resolve , 1000 ) )
138+
139+ await logger . updateProgress ( 75 , 'Finalizing results...' )
140+
141+ // Simulate final steps
142+ await new Promise ( resolve => setTimeout ( resolve , 1000 ) )
143+
144+ await logger . updateProgress ( 100 , 'Task completed successfully' )
145+ await logger . updateStatus ( 'completed' )
146+
147+ await logger . success ( `Task completed: ${ taskData . prompt } ` )
148+
149+ } catch ( error ) {
150+ console . error ( 'Error processing task:' , error )
151+ const errorMessage = error instanceof Error ? error . message : 'Unknown error occurred'
152+
153+ await logger . error ( `Error: ${ errorMessage } ` )
154+ await logger . updateStatus ( 'error' , errorMessage )
155+ }
156+ }
157+
158+ export async function DELETE ( request : NextRequest ) {
159+ try {
160+ const supabase = createServerClient ( )
161+ const url = new URL ( request . url )
162+ const action = url . searchParams . get ( 'action' )
163+
164+ if ( ! action ) {
165+ return NextResponse . json ( { error : 'Action parameter is required' } , { status : 400 } )
166+ }
167+
168+ const actions = action . split ( ',' ) . map ( ( a ) => a . trim ( ) )
169+ const validActions = [ 'completed' , 'failed' ]
170+ const invalidActions = actions . filter ( ( a ) => ! validActions . includes ( a ) )
171+
172+ if ( invalidActions . length > 0 ) {
173+ return NextResponse . json (
174+ {
175+ error : `Invalid action(s): ${ invalidActions . join ( ', ' ) } . Valid actions: ${ validActions . join ( ', ' ) } ` ,
176+ } ,
177+ { status : 400 }
178+ )
179+ }
180+
181+ // Map actions to task statuses
182+ const statusesToDelete : Task [ 'status' ] [ ] = [ ]
183+ if ( actions . includes ( 'completed' ) ) {
184+ statusesToDelete . push ( 'completed' )
185+ }
186+ if ( actions . includes ( 'failed' ) ) {
187+ statusesToDelete . push ( 'error' )
188+ }
189+
190+ if ( statusesToDelete . length === 0 ) {
191+ return NextResponse . json ( { error : 'No valid actions specified' } , { status : 400 } )
192+ }
193+
194+ // Delete tasks based on statuses
195+ const deletedCount = await deleteTasksByStatus ( supabase , statusesToDelete )
196+
197+ // Build response message
198+ const actionMessages = [ ]
199+ if ( actions . includes ( 'completed' ) ) {
200+ actionMessages . push ( 'completed' )
201+ }
202+ if ( actions . includes ( 'failed' ) ) {
203+ actionMessages . push ( 'failed' )
204+ }
205+
206+ const message = deletedCount > 0
207+ ? `${ deletedCount } ${ actionMessages . join ( ' and ' ) } task(s) deleted successfully`
208+ : 'No tasks found to delete'
209+
210+ return NextResponse . json ( {
211+ message,
212+ deletedCount,
213+ } )
214+ } catch ( error ) {
215+ console . error ( 'Error deleting tasks:' , error )
216+ return NextResponse . json ( { error : 'Failed to delete tasks' } , { status : 500 } )
217+ }
218+ }
0 commit comments