@@ -65,46 +65,114 @@ export class ScrapeService {
6565 url : string ,
6666 options : { isIframeAllowed ?: boolean } = { isIframeAllowed : true } ,
6767 ) : Promise < string | FetchPageResult > {
68- const response = await fetch ( url ) ;
68+ // Add timeout configuration for production reliability
69+ const timeoutMs = 10000 ; // 10 seconds timeout
70+ const abortController = new AbortController ( ) ;
71+ const timeoutId = setTimeout ( ( ) => abortController . abort ( ) , timeoutMs ) ;
6972
70- // Check if the response is successful
71- if ( ! response . ok ) {
72- throw new Error ( `HTTP status ${ response . status } ` ) ;
73- }
73+ console . log ( '[PERF] Scraping fetchPage started:' , {
74+ url,
75+ timeoutMs,
76+ timestamp : new Date ( ) . toISOString ( ) ,
77+ } ) ;
7478
75- // Check content type to ensure it's HTML or text
76- const contentType = response . headers . get ( 'content-type' ) || '' ;
79+ const fetchStart = Date . now ( ) ;
7780
78- // More lenient content type check - if it contains text or html in any form
79- if (
80- ! contentType . toLowerCase ( ) . includes ( 'html' ) &&
81- ! contentType . toLowerCase ( ) . includes ( 'text' ) &&
82- ! contentType . toLowerCase ( ) . includes ( 'xml' )
83- ) {
84- throw new Error (
85- `URL content type "${ contentType } " is not allowed for scraping` ,
86- ) ;
87- }
81+ try {
82+ const response = await fetch ( url , {
83+ signal : abortController . signal ,
84+ // Add additional fetch options for better reliability
85+ headers : {
86+ 'User-Agent' : 'Deepcrawl-Bot/1.0 (+https://deepcrawl.dev)' ,
87+ Accept :
88+ 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' ,
89+ 'Accept-Language' : 'en-US,en;q=0.5' ,
90+ 'Accept-Encoding' : 'gzip, deflate' ,
91+ DNT : '1' ,
92+ Connection : 'keep-alive' ,
93+ 'Upgrade-Insecure-Requests' : '1' ,
94+ } ,
95+ } ) ;
8896
89- const html = await response . text ( ) ;
97+ clearTimeout ( timeoutId ) ;
98+ const fetchTime = Date . now ( ) - fetchStart ;
9099
91- if ( ! options . isIframeAllowed ) {
92- return html ;
93- }
100+ console . log ( '[PERF] Scraping fetchPage response received:' , {
101+ url,
102+ fetchTime,
103+ status : response . status ,
104+ contentType : response . headers . get ( 'content-type' ) ,
105+ timestamp : new Date ( ) . toISOString ( ) ,
106+ } ) ;
107+
108+ // Check if the response is successful
109+ if ( ! response . ok ) {
110+ throw new Error ( `HTTP status ${ response . status } ` ) ;
111+ }
112+
113+ // Check content type to ensure it's HTML or text
114+ const contentType = response . headers . get ( 'content-type' ) || '' ;
115+
116+ // More lenient content type check - if it contains text or html in any form
117+ if (
118+ ! contentType . toLowerCase ( ) . includes ( 'html' ) &&
119+ ! contentType . toLowerCase ( ) . includes ( 'text' ) &&
120+ ! contentType . toLowerCase ( ) . includes ( 'xml' )
121+ ) {
122+ throw new Error (
123+ `URL content type "${ contentType } " is not allowed for scraping` ,
124+ ) ;
125+ }
126+
127+ const textStart = Date . now ( ) ;
128+ const html = await response . text ( ) ;
129+ const textTime = Date . now ( ) - textStart ;
130+
131+ console . log ( '[PERF] Scraping fetchPage text extracted:' , {
132+ url,
133+ textTime,
134+ htmlSize : html . length ,
135+ totalTime : Date . now ( ) - fetchStart ,
136+ timestamp : new Date ( ) . toISOString ( ) ,
137+ } ) ;
94138
95- // Extract relevant headers
96- const xFrameOptions = response . headers . get ( 'x-frame-options' ) ;
97- const contentSecurityPolicy = response . headers . get (
98- 'content-security-policy' ,
99- ) ;
139+ if ( ! options . isIframeAllowed ) {
140+ return html ;
141+ }
100142
101- // Determine if iframe embedding is allowed
102- const isIframeAllowed = this . isIframeAllowed (
103- xFrameOptions ,
104- contentSecurityPolicy ,
105- ) ;
143+ // Extract relevant headers
144+ const xFrameOptions = response . headers . get ( 'x-frame-options' ) ;
145+ const contentSecurityPolicy = response . headers . get (
146+ 'content-security-policy' ,
147+ ) ;
106148
107- return { html, isIframeAllowed } ;
149+ // Determine if iframe embedding is allowed
150+ const isIframeAllowed = this . isIframeAllowed (
151+ xFrameOptions ,
152+ contentSecurityPolicy ,
153+ ) ;
154+
155+ return { html, isIframeAllowed } ;
156+ } catch ( error ) {
157+ clearTimeout ( timeoutId ) ; // Ensure timeout is cleared on error
158+
159+ const errorTime = Date . now ( ) - fetchStart ;
160+ console . error ( '[PERF] Scraping fetchPage error:' , {
161+ url,
162+ errorTime,
163+ error : error instanceof Error ? error . message : String ( error ) ,
164+ errorName : error instanceof Error ? error . name : 'Unknown' ,
165+ isTimeout : error instanceof Error && error . name === 'AbortError' ,
166+ timestamp : new Date ( ) . toISOString ( ) ,
167+ } ) ;
168+
169+ // Provide more specific error messages
170+ if ( error instanceof Error && error . name === 'AbortError' ) {
171+ throw new Error ( `Request timeout after ${ timeoutMs } ms for URL: ${ url } ` ) ;
172+ }
173+
174+ throw error ;
175+ }
108176 }
109177
110178 private async fetchMetaFiles (
0 commit comments