moved to using worker threads, can now handle more requests because of this. Also laid foundation for passing parameters
This commit is contained in:
parent
f36999721d
commit
0626a08da8
16
README.md
16
README.md
|
@ -1,3 +1,19 @@
|
||||||
# motivational-picture-generator
|
# motivational-picture-generator
|
||||||
|
|
||||||
A project to learn NodeJS, ExpressJS, and whatever else along the way.
|
A project to learn NodeJS, ExpressJS, and whatever else along the way.
|
||||||
|
|
||||||
|
**THIS IS ALL WORK IN PROGRESS AND SUBJECT TO CHANGE**
|
||||||
|
|
||||||
|
To start server
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Make url request to `localhost:4000/submit/{}`
|
||||||
|
|
||||||
|
Testing with
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ab -c 5 -t 15 http://localhost:4000/submit/\{\}
|
||||||
|
```
|
|
@ -0,0 +1,41 @@
|
||||||
|
const puppeteer = require("puppeteer");
|
||||||
|
const { scrollPageToBottom } = require("puppeteer-autoscroll-down");
|
||||||
|
const express = require("express");
|
||||||
|
|
||||||
|
const { parentPort, workerData } = require("worker_threads");
|
||||||
|
|
||||||
|
const folderToServe =
|
||||||
|
"/Users/tarasis/Programming/websites/rmcg.dev/www/FrontendMentor/newbie/social-links-profile/";
|
||||||
|
|
||||||
|
const dynamicPage = express();
|
||||||
|
dynamicPage.use(express.static(folderToServe));
|
||||||
|
|
||||||
|
const dynamicServer = dynamicPage.listen(0, async () => {
|
||||||
|
const dynamicPort = dynamicServer.address().port;
|
||||||
|
console.log(`Dynamic server is running at http://localhost:${dynamicPort}`);
|
||||||
|
|
||||||
|
const browser = await puppeteer.launch();
|
||||||
|
const page = await browser.newPage();
|
||||||
|
await page.goto(`http://localhost:${dynamicPort}`);
|
||||||
|
|
||||||
|
await page.waitForResponse((response) => response.status() === 200);
|
||||||
|
|
||||||
|
await scrollPageToBottom(page);
|
||||||
|
|
||||||
|
const screenshotBuffer = await page.screenshot({ fullPage: true });
|
||||||
|
|
||||||
|
// other option is just to save the file to disk
|
||||||
|
// await page.screenshot({ path: 'example.png' });
|
||||||
|
// Close the browser
|
||||||
|
|
||||||
|
await browser.close();
|
||||||
|
console.log(`Closing dynamic server http://localhost:${dynamicPort}`);
|
||||||
|
|
||||||
|
await dynamicServer.close();
|
||||||
|
|
||||||
|
parentPort.postMessage({
|
||||||
|
type: "image",
|
||||||
|
data: screenshotBuffer,
|
||||||
|
port: dynamicPort,
|
||||||
|
});
|
||||||
|
});
|
72
index.mjs
72
index.mjs
|
@ -1,49 +1,61 @@
|
||||||
import puppeteer from "puppeteer";
|
|
||||||
import { scrollPageToBottom } from "puppeteer-autoscroll-down";
|
|
||||||
import express from "express";
|
import express from "express";
|
||||||
import "dotenv/config";
|
import "dotenv/config";
|
||||||
|
|
||||||
const folderToServe = "/Users/tarasis/Programming/websites/rmcg.dev/www/";
|
import { Worker, isMainThread, parentPort, workerData } from "worker_threads";
|
||||||
|
|
||||||
|
import fs from "fs";
|
||||||
|
|
||||||
const port = process.env.PORT || 4000;
|
const port = process.env.PORT || 4000;
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
app.use(express.static(folderToServe));
|
app.use(express.static("www"));
|
||||||
app.get("/screenshot", async (req, res) => {
|
|
||||||
const dynamicPage = express();
|
|
||||||
dynamicPage.use(express.static(folderToServe));
|
|
||||||
|
|
||||||
const dynamicServer = dynamicPage.listen(0, async () => {
|
app.get("/submit/:mpgParams", (req, res) => {
|
||||||
const dynamicPort = dynamicServer.address().port;
|
// console.log("🚀 ~ app.get ~ req:", req);
|
||||||
console.log(
|
// Get any supplied parameters, this keeps it clean
|
||||||
`Dynamic server is running at http://localhost:${dynamicPort}`
|
// if I pass more parameters in the future
|
||||||
);
|
const mpgParams = req.query.mpgParams;
|
||||||
|
console.log("🚀 ~ app.get ~ mpgParams:", mpgParams);
|
||||||
|
|
||||||
const browser = await puppeteer.launch();
|
const worker = new Worker("./image-generator.cjs", {
|
||||||
const page = await browser.newPage();
|
workerData: {
|
||||||
await page.goto(`http://localhost:${dynamicPort}`);
|
mpgParams,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
await page.waitForResponse((response) => response.status() === 200);
|
// nicer would be to return a link the user
|
||||||
|
// can click to download the image
|
||||||
await scrollPageToBottom(page);
|
// or better yet dynamically load it into the original page in an iframe or ssg thing
|
||||||
|
// but that might require moving project
|
||||||
const screenshotBuffer = await page.screenshot({ fullPage: true });
|
// to a framework like react/astro
|
||||||
|
worker.on("message", (message) => {
|
||||||
// Respond with the image
|
if (message.type === "image") {
|
||||||
res.writeHead(200, {
|
res.writeHead(200, {
|
||||||
"Content-Type": "image/png",
|
"Content-Type": "image/png",
|
||||||
"Content-Length": screenshotBuffer.length,
|
"Content-Length": message.data.length,
|
||||||
});
|
});
|
||||||
res.end(screenshotBuffer);
|
res.end(message.data);
|
||||||
// other option is just to save the file to disk
|
|
||||||
// await page.screenshot({ path: 'example.png' });
|
|
||||||
// Close the browser
|
|
||||||
|
|
||||||
await browser.close();
|
// save the image - temporary for now
|
||||||
console.log(`Closing dynamic server http://localhost:${dynamicPort}`);
|
// fs.writeFileSync(
|
||||||
|
// `received_image-${message.port}.png`,
|
||||||
|
// message.data
|
||||||
|
// );
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
await dynamicServer.close();
|
worker.on("error", (err) => {
|
||||||
|
// Handle errors from the worker
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).send("Internal Server Error");
|
||||||
|
});
|
||||||
|
|
||||||
|
worker.on("exit", (code) => {
|
||||||
|
if (code !== 0) {
|
||||||
|
// Handle non-zero exit codes
|
||||||
|
console.error(new Error(`Worker stopped with exit code ${code}`));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"express": "^4.18.3",
|
"express": "^4.18.3",
|
||||||
"puppeteer": "^22.4.0",
|
"puppeteer": "^22.4.0",
|
||||||
"puppeteer-autoscroll-down": "^2.0.0"
|
"puppeteer-autoscroll-down": "^1.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
|
@ -1498,11 +1498,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/puppeteer-autoscroll-down": {
|
"node_modules/puppeteer-autoscroll-down": {
|
||||||
"version": "2.0.0",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/puppeteer-autoscroll-down/-/puppeteer-autoscroll-down-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/puppeteer-autoscroll-down/-/puppeteer-autoscroll-down-1.1.2.tgz",
|
||||||
"integrity": "sha512-Bo5ADSo1t5u36IxY4x/EgS6kNQqMXfTNRSh5CSR5bpoTTOpzE+HE0LZ5xIlqzAfpRjaPtsOd+2lRQiPeAKQlHA==",
|
"integrity": "sha512-SSzxBf6Iu2zn6NNqZO1XlU8cqbjVOzhlTh38xLU42srNZjrDXkIAhKandScM0ZwueVOtOs4xSY0FN2S6whlFSA==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/puppeteer-core": {
|
"node_modules/puppeteer-core": {
|
||||||
|
|
|
@ -15,6 +15,6 @@
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"express": "^4.18.3",
|
"express": "^4.18.3",
|
||||||
"puppeteer": "^22.4.0",
|
"puppeteer": "^22.4.0",
|
||||||
"puppeteer-autoscroll-down": "^2.0.0"
|
"puppeteer-autoscroll-down": "^1.1.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Movitvational Picture Generator</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>Hello World</h1>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
Loading…
Reference in New Issue