11const API_BASE_URL = 'http://127.0.0.1:8000' ;
22
3+ const resolveRequestTimeout = ( ) => {
4+ const env = typeof import . meta !== 'undefined' ? import . meta. env : undefined ;
5+ const configured = env ?. VITE_API_TIMEOUT_MS ;
6+ const parsed = Number . parseInt ( configured , 10 ) ;
7+ if ( Number . isFinite ( parsed ) && parsed > 0 ) {
8+ return parsed ;
9+ }
10+ return 15000 ;
11+ } ;
12+
13+ const REQUEST_TIMEOUT_MS = resolveRequestTimeout ( ) ; // default to 15s, overridable via Vite env
14+
315class ApiError extends Error {
416 constructor ( message , status ) {
517 super ( message ) ;
@@ -19,12 +31,31 @@ async function handleResponse(response) {
1931 return response . json ( ) ;
2032}
2133
34+ async function fetchWithTimeout ( url , options = { } , timeout = REQUEST_TIMEOUT_MS ) {
35+ const controller = new AbortController ( ) ;
36+ const timeoutId = setTimeout ( ( ) => controller . abort ( ) , timeout ) ;
37+
38+ try {
39+ return await fetch ( url , { ...options , signal : controller . signal } ) ;
40+ } catch ( error ) {
41+ if ( error . name === 'AbortError' ) {
42+ throw new ApiError ( 'Request timed out' , 408 ) ;
43+ }
44+ throw error ;
45+ } finally {
46+ clearTimeout ( timeoutId ) ;
47+ }
48+ }
49+
2250export const apiService = {
2351 async getConversationHistory ( ) {
2452 try {
25- const res = await fetch ( `${ API_BASE_URL } /get-conversation-history` ) ;
53+ const res = await fetchWithTimeout ( `${ API_BASE_URL } /get-conversation-history` ) ;
2654 return handleResponse ( res ) ;
2755 } catch ( error ) {
56+ if ( error instanceof ApiError ) {
57+ throw error ;
58+ }
2859 throw new ApiError (
2960 'Failed to fetch conversation history' ,
3061 error . status || 500
@@ -38,7 +69,7 @@ export const apiService = {
3869 }
3970
4071 try {
41- const res = await fetch (
72+ const res = await fetchWithTimeout (
4273 `${ API_BASE_URL } /send-prompt?prompt=${ encodeURIComponent ( message ) } ` ,
4374 {
4475 method : 'POST' ,
@@ -49,6 +80,9 @@ export const apiService = {
4980 ) ;
5081 return handleResponse ( res ) ;
5182 } catch ( error ) {
83+ if ( error instanceof ApiError ) {
84+ throw error ;
85+ }
5286 throw new ApiError (
5387 'Failed to send message' ,
5488 error . status || 500
@@ -58,7 +92,7 @@ export const apiService = {
5892
5993 async startWorkflow ( ) {
6094 try {
61- const res = await fetch (
95+ const res = await fetchWithTimeout (
6296 `${ API_BASE_URL } /start-workflow` ,
6397 {
6498 method : 'POST' ,
@@ -69,6 +103,9 @@ export const apiService = {
69103 ) ;
70104 return handleResponse ( res ) ;
71105 } catch ( error ) {
106+ if ( error instanceof ApiError ) {
107+ throw error ;
108+ }
72109 throw new ApiError (
73110 'Failed to start workflow' ,
74111 error . status || 500
@@ -78,18 +115,21 @@ export const apiService = {
78115
79116 async confirm ( ) {
80117 try {
81- const res = await fetch ( `${ API_BASE_URL } /confirm` , {
118+ const res = await fetchWithTimeout ( `${ API_BASE_URL } /confirm` , {
82119 method : 'POST' ,
83120 headers : {
84121 'Content-Type' : 'application/json'
85122 }
86123 } ) ;
87124 return handleResponse ( res ) ;
88125 } catch ( error ) {
126+ if ( error instanceof ApiError ) {
127+ throw error ;
128+ }
89129 throw new ApiError (
90130 'Failed to confirm action' ,
91131 error . status || 500
92132 ) ;
93133 }
94134 }
95- } ;
135+ } ;
0 commit comments