1
1
import puppeteer from 'puppeteer-core' ;
2
2
import chromium from 'chrome-aws-lambda' ;
3
3
import middleware from './_common/middleware.js' ;
4
+ import { exec } from 'child_process' ;
5
+ import { promises as fs } from 'fs' ;
6
+ import path from 'path' ;
7
+ import pkg from 'uuid' ;
8
+ const { v4 : uuidv4 } = pkg ;
4
9
5
- const screenshotHandler = async ( targetUrl ) => {
10
+ // Helper function for direct chromium screenshot as fallback
11
+ const directChromiumScreenshot = async ( url ) => {
12
+ console . log ( `[DIRECT-SCREENSHOT] Starting direct screenshot process for URL: ${ url } ` ) ;
13
+
14
+ // Create a tmp filename
15
+ const tmpDir = '/tmp' ;
16
+ const uuid = uuidv4 ( ) ;
17
+ const screenshotPath = path . join ( tmpDir , `screenshot-${ uuid } .png` ) ;
18
+
19
+ console . log ( `[DIRECT-SCREENSHOT] Will save screenshot to: ${ screenshotPath } ` ) ;
20
+
21
+ return new Promise ( ( resolve , reject ) => {
22
+ const chromePath = process . env . CHROME_PATH || '/usr/bin/chromium' ;
23
+ const command = `${ chromePath } --headless --disable-gpu --no-sandbox --screenshot=${ screenshotPath } "${ url } "` ;
24
+
25
+ console . log ( `[DIRECT-SCREENSHOT] Executing command: ${ command } ` ) ;
26
+
27
+ exec ( command , async ( error , stdout , stderr ) => {
28
+ if ( error ) {
29
+ console . error ( `[DIRECT-SCREENSHOT] Error executing Chromium: ${ error . message } ` ) ;
30
+ return reject ( error ) ;
31
+ }
32
+
33
+ try {
34
+ // Read screenshot
35
+ const screenshotData = await fs . readFile ( screenshotPath ) ;
36
+ console . log ( `[DIRECT-SCREENSHOT] Read ${ screenshotData . length } bytes from screenshot file` ) ;
37
+
38
+ // Convert base64
39
+ const base64Data = screenshotData . toString ( 'base64' ) ;
40
+
41
+ // Clean
42
+ await fs . unlink ( screenshotPath ) . catch ( err =>
43
+ console . warn ( `[DIRECT-SCREENSHOT] Failed to delete temp file: ${ err . message } ` )
44
+ ) ;
45
+
46
+ resolve ( base64Data ) ;
47
+ } catch ( readError ) {
48
+ console . error ( `[DIRECT-SCREENSHOT] Error reading screenshot: ${ readError . message } ` ) ;
49
+ reject ( readError ) ;
50
+ }
51
+ } ) ;
52
+ } ) ;
53
+ } ;
6
54
55
+ const screenshotHandler = async ( targetUrl ) => {
56
+ console . log ( `[SCREENSHOT] Request received for URL: ${ targetUrl } ` ) ;
57
+
7
58
if ( ! targetUrl ) {
59
+ console . error ( '[SCREENSHOT] URL is missing from queryStringParameters' ) ;
8
60
throw new Error ( 'URL is missing from queryStringParameters' ) ;
9
61
}
10
-
62
+
11
63
if ( ! targetUrl . startsWith ( 'http://' ) && ! targetUrl . startsWith ( 'https://' ) ) {
12
64
targetUrl = 'http://' + targetUrl ;
13
65
}
14
-
66
+
15
67
try {
16
68
new URL ( targetUrl ) ;
17
69
} catch ( error ) {
70
+ console . error ( `[SCREENSHOT] URL provided is invalid: ${ targetUrl } ` ) ;
18
71
throw new Error ( 'URL provided is invalid' ) ;
19
72
}
20
73
74
+ // First try direct Chromium
75
+ try {
76
+ console . log ( `[SCREENSHOT] Using direct Chromium method for URL: ${ targetUrl } ` ) ;
77
+ const base64Screenshot = await directChromiumScreenshot ( targetUrl ) ;
78
+ console . log ( `[SCREENSHOT] Direct screenshot successful` ) ;
79
+ return { image : base64Screenshot } ;
80
+ } catch ( directError ) {
81
+ console . error ( `[SCREENSHOT] Direct screenshot method failed: ${ directError . message } ` ) ;
82
+ console . log ( `[SCREENSHOT] Falling back to puppeteer method...` ) ;
83
+ }
84
+
85
+ // fall back puppeteer
21
86
let browser = null ;
22
87
try {
23
- browser = await puppeteer . launch ( {
88
+ console . log ( `[SCREENSHOT] Launching puppeteer browser` ) ;
89
+ browser = await puppeteer . launch ( {
24
90
args : [ ...chromium . args , '--no-sandbox' ] , // Add --no-sandbox flag
25
91
defaultViewport : { width : 800 , height : 600 } ,
26
- executablePath : process . env . CHROME_PATH || await chromium . executablePath ,
27
- headless : chromium . headless ,
92
+ executablePath : process . env . CHROME_PATH || '/usr/bin/ chromium' ,
93
+ headless : true ,
28
94
ignoreHTTPSErrors : true ,
29
95
ignoreDefaultArgs : [ '--disable-extensions' ] ,
30
96
} ) ;
31
-
97
+
98
+ console . log ( `[SCREENSHOT] Creating new page` ) ;
32
99
let page = await browser . newPage ( ) ;
33
-
100
+
101
+ console . log ( `[SCREENSHOT] Setting page preferences` ) ;
34
102
await page . emulateMediaFeatures ( [ { name : 'prefers-color-scheme' , value : 'dark' } ] ) ;
35
103
page . setDefaultNavigationTimeout ( 8000 ) ;
104
+
105
+ console . log ( `[SCREENSHOT] Navigating to URL: ${ targetUrl } ` ) ;
36
106
await page . goto ( targetUrl , { waitUntil : 'domcontentloaded' } ) ;
37
-
107
+
108
+ console . log ( `[SCREENSHOT] Checking if body element exists` ) ;
38
109
await page . evaluate ( ( ) => {
39
110
const selector = 'body' ;
40
111
return new Promise ( ( resolve , reject ) => {
@@ -45,14 +116,21 @@ const screenshotHandler = async (targetUrl) => {
45
116
resolve ( ) ;
46
117
} ) ;
47
118
} ) ;
48
-
119
+
120
+ console . log ( `[SCREENSHOT] Taking screenshot` ) ;
49
121
const screenshotBuffer = await page . screenshot ( ) ;
122
+
123
+ console . log ( `[SCREENSHOT] Converting screenshot to base64` ) ;
50
124
const base64Screenshot = screenshotBuffer . toString ( 'base64' ) ;
51
-
125
+
126
+ console . log ( `[SCREENSHOT] Screenshot complete, returning image` ) ;
52
127
return { image : base64Screenshot } ;
53
-
128
+ } catch ( error ) {
129
+ console . error ( `[SCREENSHOT] Puppeteer screenshot failed: ${ error . message } ` ) ;
130
+ throw error ;
54
131
} finally {
55
132
if ( browser !== null ) {
133
+ console . log ( `[SCREENSHOT] Closing browser` ) ;
56
134
await browser . close ( ) ;
57
135
}
58
136
}
0 commit comments