fix: puppeteer

This commit is contained in:
Val 2025-07-08 02:47:50 +07:00
parent 5c3d55966e
commit 8f1d4f979c
3 changed files with 102 additions and 50 deletions

View File

@ -1,6 +1,18 @@
// app/api/seo-check/route.js
import { NextResponse } from "next/server";
import puppeteer from "puppeteer";
import puppeteer from "puppeteer-core";
// Helper function to get Chrome executable path
function getChromePath() {
if (process.env.AWS_LAMBDA_FUNCTION_VERSION) {
return "/usr/bin/chromium-browser";
}
return process.platform === "win32"
? "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
: process.platform === "darwin"
? "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
: "/usr/bin/google-chrome";
}
export async function POST(request) {
const startTime = Date.now();
@ -37,20 +49,31 @@ export async function POST(request) {
let browser;
try {
browser = await puppeteer.launch({
headless: true,
executablePath: process.env.NEXT_CHROMIUM_PATH || '/usr/bin/chromium-browser',
headless: "new",
args: [
"--no-sandbox",
"--disable-setuid-sandbox",
"--disable-dev-shm-usage",
"--disable-accelerated-2d-canvas",
"--no-first-run",
"--no-zygote",
"--single-process",
"--disable-gpu",
],
timeout: 15000,
});
} catch (e) {
console.error("Browser launch error:", e);
return NextResponse.json(
{ error: "Failed to initialize browser" },
{ error: `Failed to initialize browser: ${e.message}` },
{ status: 500 }
);
}
let page;
let finalUrl;
let html;
let securityHeaders = {};
let response;
try {
page = await browser.newPage();
@ -63,7 +86,6 @@ export async function POST(request) {
// Listen for response to capture headers
page.on("response", async (res) => {
if (res.url() === url || res.url() === finalUrl) {
response = res;
securityHeaders = {
https: res.url().startsWith("https://"),
xFrameOptions: res.headers()["x-frame-options"],
@ -82,11 +104,9 @@ export async function POST(request) {
// Get final URL after potential redirects
finalUrl = page.url();
// Get the full HTML content
html = await page.content();
} catch (e) {
await browser.close();
console.error("Page navigation error:", e);
return NextResponse.json(
{
error:
@ -98,53 +118,66 @@ export async function POST(request) {
);
}
// Use Puppeteer's DOM methods for analysis
const analysis = {
url: finalUrl,
pageLoadTime: (Date.now() - startTime) / 1000,
title: await analyzeTitle(page),
meta: {
description: await analyzeMetaDescription(page),
robots: await analyzeMetaRobots(page),
viewport: await analyzeViewport(page),
charset: await analyzeCharset(page),
keywords: await getMetaContent(page, "keywords"),
},
headings: await analyzeHeadings(page),
images: await analyzeImages(page),
links: await analyzeLinks(page, finalUrl),
content: await analyzeContent(page),
technical: {
canonical: await analyzeCanonical(page),
language: await analyzeLanguage(page),
schemaMarkup: await analyzeSchemaMarkup(page),
doctype: await analyzeDoctype(page),
},
social: {
openGraph: {
title: await getMetaContent(page, "og:title"),
description: await getMetaContent(page, "og:description"),
image: await getMetaContent(page, "og:image"),
url: await getMetaContent(page, "og:url"),
try {
// Use Puppeteer's DOM methods for analysis
const analysis = {
url: finalUrl,
pageLoadTime: (Date.now() - startTime) / 1000,
title: await analyzeTitle(page),
meta: {
description: await analyzeMetaDescription(page),
robots: await analyzeMetaRobots(page),
viewport: await analyzeViewport(page),
charset: await analyzeCharset(page),
keywords: await getMetaContent(page, "keywords"),
},
twitterCard: {
card: await getMetaContent(page, "twitter:card"),
title: await getMetaContent(page, "twitter:title"),
description: await getMetaContent(page, "twitter:description"),
image: await getMetaContent(page, "twitter:image"),
headings: await analyzeHeadings(page),
images: await analyzeImages(page),
links: await analyzeLinks(page, finalUrl),
content: await analyzeContent(page),
technical: {
canonical: await analyzeCanonical(page),
language: await analyzeLanguage(page),
schemaMarkup: await analyzeSchemaMarkup(page),
doctype: await analyzeDoctype(page),
},
},
security: securityHeaders,
analyzedAt: new Date().toISOString(),
};
social: {
openGraph: {
title: await getMetaContent(page, "og:title"),
description: await getMetaContent(page, "og:description"),
image: await getMetaContent(page, "og:image"),
url: await getMetaContent(page, "og:url"),
},
twitterCard: {
card: await getMetaContent(page, "twitter:card"),
title: await getMetaContent(page, "twitter:title"),
description: await getMetaContent(page, "twitter:description"),
image: await getMetaContent(page, "twitter:image"),
},
},
security: securityHeaders,
analyzedAt: new Date().toISOString(),
};
await browser.close();
return NextResponse.json(analysis);
await browser.close();
return NextResponse.json(analysis);
} catch (error) {
await browser.close();
console.error("SEO analysis error:", error);
return NextResponse.json(
{
error: "Internal server error during analysis",
details:
process.env.NODE_ENV === "development" ? error.stack : undefined,
},
{ status: 500 }
);
}
} catch (error) {
console.error("SEO analysis error:", error);
console.error("Unexpected error:", error);
return NextResponse.json(
{
error: "Internal server error during analysis",
error: "Internal server error",
details:
process.env.NODE_ENV === "development" ? error.stack : undefined,
},

18
package-lock.json generated
View File

@ -29,6 +29,7 @@
"node-fetch": "^3.3.2",
"postcss": "^8.5.6",
"puppeteer": "^24.11.2",
"puppeteer-core": "^24.12.0",
"react": "18.2.0",
"react-curved-text": "^3.0.0",
"react-dom": "18.2.0",
@ -5149,6 +5150,23 @@
}
},
"node_modules/puppeteer-core": {
"version": "24.12.0",
"resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.12.0.tgz",
"integrity": "sha512-VrPXPho5Q90Ao86FwJVb+JeAF2Tf41wOTGg8k2SyQJePiJ6hJ5iujYpmP+bmhlb6o+J26bQYRDPOYXP7ALWcxQ==",
"license": "Apache-2.0",
"dependencies": {
"@puppeteer/browsers": "2.10.5",
"chromium-bidi": "5.1.0",
"debug": "^4.4.1",
"devtools-protocol": "0.0.1464554",
"typed-query-selector": "^2.12.0",
"ws": "^8.18.3"
},
"engines": {
"node": ">=18"
}
},
"node_modules/puppeteer/node_modules/puppeteer-core": {
"version": "24.11.2",
"resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.11.2.tgz",
"integrity": "sha512-c49WifNb8hix+gQH17TldmD6TC/Md2HBaTJLHexIUq4sZvo2pyHY/Pp25qFQjibksBu/SJRYUY7JsoaepNbiRA==",

View File

@ -32,6 +32,7 @@
"node-fetch": "^3.3.2",
"postcss": "^8.5.6",
"puppeteer": "^24.11.2",
"puppeteer-core": "^24.12.0",
"react": "18.2.0",
"react-curved-text": "^3.0.0",
"react-dom": "18.2.0",