| const express = require('express'); |
| const { chromium } = require('playwright'); |
| const cheerio = require('cheerio'); |
| const cors = require('cors'); |
|
|
| const app = express(); |
| app.use(cors()); |
| app.use(express.json()); |
|
|
| const requestQueue = []; |
| let isProcessing = false; |
| let browserInstance = null; |
|
|
| |
| async function initBrowser() { |
| try { |
| console.log("[System Init]: Launching master Chromium process..."); |
| browserInstance = await chromium.launch({ |
| headless: true, |
| args: [ |
| '--no-sandbox', |
| '--disable-setuid-sandbox', |
| '--disable-dev-shm-usage', |
| '--disable-accelerated-2d-canvas', |
| '--disable-gpu' |
| ] |
| }); |
| console.log("[System Init]: Headless Chromium ready to snatch."); |
| } catch (err) { |
| console.error("[System Init Error]: Failed launching master browser:", err.message); |
| } |
| } |
|
|
| |
| async function processQueue() { |
| if (isProcessing || requestQueue.length === 0) return; |
| |
| isProcessing = true; |
| const currentTask = requestQueue.shift(); |
|
|
| |
| if (!browserInstance) { |
| await initBrowser(); |
| } |
|
|
| let context = null; |
| let page = null; |
|
|
| try { |
| const { targetUrl, requestedAssets, res } = currentTask; |
| console.log(`\n[Snatch Worker]: Processing -> ${targetUrl}`); |
|
|
| |
| context = await browserInstance.newContext({ |
| userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36', |
| viewport: { width: 1280, height: 800 }, |
| deviceScaleFactor: 1, |
| bypassCSP: true |
| }); |
|
|
| page = await context.newPage(); |
|
|
| |
| await page.goto(targetUrl, { |
| timeout: 25000, |
| waitUntil: 'networkidle' |
| }); |
|
|
| |
| const renderedHtml = await page.content(); |
| const $ = cheerio.load(renderedHtml); |
| |
| let extractedCss = ""; |
| let extractedJs = ""; |
|
|
| |
| if (requestedAssets.includes('css')) { |
| $('style').each((_, el) => { |
| extractedCss += `/* --- Inline Style --- */\n${$(el).text()}\n\n`; |
| }); |
| $('link[rel="stylesheet"]').each((_, el) => { |
| const href = $(el).attr('href'); |
| if (href) { |
| const absUrl = href.startsWith('http') ? href : new URL(href, targetUrl).href; |
| extractedCss += `/* External Reference: ${absUrl} */\n`; |
| } |
| }); |
| } |
|
|
| |
| if (requestedAssets.includes('js')) { |
| $('script').each((_, el) => { |
| const text = $(el).text(); |
| const src = $(el).attr('src'); |
| if (text.trim()) { |
| extractedJs += `// --- Inline Script ---\n${text.trim()}\n\n`; |
| } else if (src) { |
| const absUrl = src.startsWith('http') ? src : new URL(src, targetUrl).href; |
| extractedJs += `// External Reference: ${absUrl}\n`; |
| } |
| }); |
| } |
|
|
| |
| let cleanHtml = ""; |
| if (requestedAssets.includes('html')) { |
| $('script, style, link[rel="stylesheet"]').remove(); |
| cleanHtml = $('body').html() || $.html(); |
| } |
|
|
| |
| res.json({ |
| html: requestedAssets.includes('html') ? cleanHtml.trim() : null, |
| css: requestedAssets.includes('css') ? extractedCss.trim() : null, |
| js: requestedAssets.includes('js') ? extractedJs.trim() : null |
| }); |
|
|
| console.log(`[Snatch Worker]: Successfully sent assets back to frontend client.`); |
|
|
| } catch (error) { |
| console.error(`[Snatch Worker Error]: Execution hit a wall:`, error.message); |
| currentTask.res.status(500).json({ error: "Playwright Snatcher failed execution task: " + error.message }); |
| } finally { |
| |
| if (page) await page.close().catch(() => {}); |
| if (context) await context.close().catch(() => {}); |
| |
| isProcessing = false; |
| |
| setTimeout(processQueue, 150); |
| } |
| } |
|
|
| |
| app.get('/', (req, res) => { |
| res.json({ |
| engine: "CodeSnatch Ultimate Playwright Cluster", |
| status: "operational", |
| browserProcessAlive: !!browserInstance, |
| backlogLength: requestQueue.length |
| }); |
| }); |
|
|
| |
| app.get('/scrape', (req, res) => { |
| const targetUrl = req.query.url; |
| const requestedAssets = req.query.assets ? req.query.assets.split(',') : ['html', 'css', 'js']; |
|
|
| if (!targetUrl) { |
| return res.status(400).json({ error: "Missing required query parameter 'url'." }); |
| } |
|
|
| requestQueue.push({ targetUrl, requestedAssets, res }); |
| console.log(`[Traffic]: Enqueued new web page target. Backlog count: ${requestQueue.length}`); |
| |
| processQueue(); |
| }); |
|
|
| |
| process.on('SIGTERM', async () => { |
| if (browserInstance) await browserInstance.close(); |
| process.exit(0); |
| }); |
|
|
| const PORT = process.env.PORT || 7860; |
| app.listen(PORT, async () => { |
| console.log(`=== Ultimate CodeSnatch Backend Listening on Port: ${PORT} ===`); |
| await initBrowser(); |
| }); |
|
|