1+ import { NextRequest , NextResponse } from "next/server" ;
2+
3+ const BACKEND_URL = process . env . NEXT_PUBLIC_VALIDATE_BACKEND_URL || "http://localhost:8000" ;
4+
5+ export async function POST ( request : NextRequest ) {
6+ console . log ( "🚨 USING ENHANCED ROUTE HANDLER - VERSION 2 🚨" ) ;
7+
8+ try {
9+ const body = await request . json ( ) ;
10+ const { playbook_content, profile = 'production' } = body ;
11+
12+ if ( ! playbook_content || ! playbook_content . trim ( ) ) {
13+ return NextResponse . json (
14+ { error : "No playbook content provided" } ,
15+ { status : 400 }
16+ ) ;
17+ }
18+
19+ // Use the correct working endpoint
20+ const streamingUrl = `${ BACKEND_URL } /api/validate/playbook/stream` ;
21+ console . log ( "🔗 Connecting to validation service:" , streamingUrl ) ;
22+ console . log ( "📤 Request payload:" , { playbook_length : playbook_content . length , profile } ) ;
23+
24+ try {
25+ const response = await fetch ( streamingUrl , {
26+ method : "POST" ,
27+ headers : {
28+ "Content-Type" : "application/json" ,
29+ "Accept" : "text/event-stream" ,
30+ } ,
31+ body : JSON . stringify ( {
32+ playbook_content,
33+ profile,
34+ } ) ,
35+ // Add timeout to prevent hanging
36+ signal : AbortSignal . timeout ( 120000 ) , // 2 minute timeout
37+ } ) ;
38+
39+ console . log ( "📡 Backend response status:" , response . status ) ;
40+ console . log ( "📡 Backend response headers:" , Object . fromEntries ( response . headers . entries ( ) ) ) ;
41+
42+ if ( ! response . ok ) {
43+ const errorText = await response . text ( ) ;
44+ console . error ( "❌ Backend validation error:" , errorText ) ;
45+
46+ return NextResponse . json ( {
47+ passed : false ,
48+ summary : `Validation service error: ${ response . status } ` ,
49+ issues : [ ] ,
50+ raw_output : errorText ,
51+ error_message : `Backend validation error: ${ response . status } - ${ errorText } ` ,
52+ debug_info : {
53+ status : "error" ,
54+ error_code : response . status ,
55+ playbook_length : playbook_content . length
56+ }
57+ } , { status : 200 } ) ;
58+ }
59+
60+ const contentType = response . headers . get ( "content-type" ) ;
61+
62+ // Handle direct JSON response (fallback)
63+ if ( contentType ?. includes ( "application/json" ) ) {
64+ console . log ( "📊 Received JSON response from streaming endpoint" ) ;
65+ const data = await response . json ( ) ;
66+ return NextResponse . json ( data ) ;
67+ }
68+
69+ // Handle streaming response - the main path
70+ if ( contentType ?. includes ( "text/event-stream" ) || contentType ?. includes ( "text/plain" ) ) {
71+ console . log ( "🌊 Handling streaming response..." ) ;
72+
73+ const stream = new ReadableStream ( {
74+ async start ( controller ) {
75+ const reader = response . body ?. getReader ( ) ;
76+ if ( ! reader ) {
77+ controller . close ( ) ;
78+ return ;
79+ }
80+
81+ const decoder = new TextDecoder ( ) ;
82+ let buffer = '' ;
83+
84+ try {
85+ while ( true ) {
86+ const { done, value } = await reader . read ( ) ;
87+
88+ if ( done ) {
89+ console . log ( "✅ Stream completed successfully" ) ;
90+ controller . close ( ) ;
91+ break ;
92+ }
93+
94+ if ( ! value ) {
95+ continue ;
96+ }
97+
98+ buffer += decoder . decode ( value , { stream : true } ) ;
99+
100+ // Process complete lines
101+ const lines = buffer . split ( '\n' ) ;
102+ buffer = lines . pop ( ) || '' ; // Keep incomplete line in buffer
103+
104+ for ( const line of lines ) {
105+ const trimmedLine = line . trim ( ) ;
106+ if ( ! trimmedLine ) continue ;
107+
108+ try {
109+ let data ;
110+
111+ // Handle SSE format
112+ if ( trimmedLine . startsWith ( 'data: ' ) ) {
113+ const dataStr = trimmedLine . slice ( 6 ) ;
114+ if ( dataStr === '[DONE]' ) {
115+ console . log ( "🏁 Received [DONE] signal" ) ;
116+ controller . close ( ) ;
117+ return ;
118+ }
119+ data = JSON . parse ( dataStr ) ;
120+ } else {
121+ // Try parsing as direct JSON
122+ data = JSON . parse ( trimmedLine ) ;
123+ }
124+
125+ // Forward the parsed data as SSE format
126+ const sseData = `data: ${ JSON . stringify ( data ) } \n\n` ;
127+ controller . enqueue ( new TextEncoder ( ) . encode ( sseData ) ) ;
128+
129+ } catch ( parseError ) {
130+ console . warn ( "⚠️ Failed to parse line:" , trimmedLine , parseError ) ;
131+ continue ;
132+ }
133+ }
134+ }
135+ } catch ( error ) {
136+ console . error ( "❌ Stream reading error:" , error ) ;
137+ controller . error ( error ) ;
138+ } finally {
139+ try {
140+ reader . releaseLock ( ) ;
141+ } catch ( lockError ) {
142+ console . warn ( "⚠️ Error releasing reader lock:" , lockError ) ;
143+ }
144+ }
145+ }
146+ } ) ;
147+
148+ return new NextResponse ( stream , {
149+ headers : {
150+ "Content-Type" : "text/event-stream" ,
151+ "Cache-Control" : "no-cache" ,
152+ "Connection" : "keep-alive" ,
153+ "Access-Control-Allow-Origin" : "*" ,
154+ "Access-Control-Allow-Methods" : "GET, POST, OPTIONS" ,
155+ "Access-Control-Allow-Headers" : "Content-Type" ,
156+ } ,
157+ } ) ;
158+ }
159+
160+ // Unexpected content type, try to handle as text
161+ const textResponse = await response . text ( ) ;
162+ console . warn ( "🤔 Unexpected content type, got text:" , textResponse . substring ( 0 , 200 ) ) ;
163+
164+ try {
165+ const jsonData = JSON . parse ( textResponse ) ;
166+ return NextResponse . json ( jsonData ) ;
167+ } catch {
168+ // If it's not JSON, treat as error
169+ throw new Error ( `Unexpected response format: ${ textResponse . substring ( 0 , 100 ) } ` ) ;
170+ }
171+
172+ } catch ( fetchError ) {
173+ console . error ( "❌ Backend connection failed:" , fetchError ) ;
174+
175+ return NextResponse . json ( {
176+ passed : false ,
177+ summary : "Validation service unavailable" ,
178+ issues : [ ] ,
179+ raw_output : `Service Error: ${ fetchError instanceof Error ? fetchError . message : 'Unknown error' } ` ,
180+ error_message : "Unable to connect to validation service. Please check if the backend is running." ,
181+ debug_info : {
182+ status : "service_unavailable" ,
183+ playbook_length : playbook_content . length ,
184+ error : "Backend connection failed"
185+ }
186+ } , { status : 200 } ) ;
187+ }
188+
189+ } catch ( error ) {
190+ console . error ( "❌ Validation proxy error:" , error ) ;
191+ return NextResponse . json (
192+ {
193+ error : "Validation service unavailable" ,
194+ detail : error instanceof Error ? error . message : "Unknown error" ,
195+ passed : false ,
196+ issues : [ ] ,
197+ raw_output : "" ,
198+ debug_info : {
199+ error : "Validation service error" ,
200+ status : "failed"
201+ }
202+ } ,
203+ { status : 500 }
204+ ) ;
205+ }
206+ }
207+
208+ // Handle OPTIONS requests for CORS
209+ export async function OPTIONS ( request : NextRequest ) {
210+ return new NextResponse ( null , {
211+ status : 200 ,
212+ headers : {
213+ "Access-Control-Allow-Origin" : "*" ,
214+ "Access-Control-Allow-Methods" : "GET, POST, OPTIONS" ,
215+ "Access-Control-Allow-Headers" : "Content-Type" ,
216+ } ,
217+ } ) ;
218+ }
0 commit comments