screenshot-service/index.js

165 lines
4.7 KiB
JavaScript
Raw Normal View History

2022-04-24 17:24:27 +02:00
var serve = require("koa-static");
const Koa = require("koa");
const Router = require("@koa/router");
const mount = require("koa-mount");
const qs = require("qs");
const { Readable } = require("stream");
const { spawn } = require("child_process");
2022-05-05 21:54:34 +02:00
const { requests } = require("./memory");
const ScreenshotRequest = require("./request");
const DOCKER_ARGS = require("./docker-args");
2022-04-24 17:24:27 +02:00
const router = new Router();
// response
const app = new Koa();
const static = new Koa();
static.use(serve("./static"));
app.use(mount("/static", static));
function attach(docker_id, output_stream) {
// to prevent browser timeout
const interval = setInterval(() => output_stream.push("<span></span>"), 500);
const task = spawn("docker", ["logs", "-f", docker_id]);
2022-04-24 19:57:01 +02:00
task.stdout.on("data", (d) => {
output_stream.push(d);
console.log("DATA!", d.toString());
});
2022-04-24 20:04:45 +02:00
task.stderr.on("data", (d) => {
/* output_stream.push(d); */
console.log("STDERR!", d.toString());
});
2022-04-24 19:57:01 +02:00
task.stdout.on("error", (d) => {
output_stream.push(d);
});
2022-04-24 17:24:27 +02:00
task.on("close", () => {
2022-04-24 20:35:24 +02:00
output_stream.push("</pre>");
output_stream.push(/* HTML */ `<script>
clearInterval(window.interval);
</script>`);
clearInterval(interval);
output_stream.push(null);
2022-04-24 17:24:27 +02:00
});
}
router.get("/", async (ctx) => {
2022-05-25 19:27:12 +02:00
ctx.body = /* HTML */ `<!DOCTYPE html>
<html>
<body>
<form onsubmit="formSubmit(event)">
<label for="url_input">URL:</label>
<input type="text" name="url" id="url_input" />
<br />
<label for="domains">Domeny (oddzielone przecinkami):</label>
<input
type="text"
name="domains"
id="domains"
value="doubleclick.net,facebook.com"
/>
<br />
<input type="submit" />
</form>
<code><pre id="output"></pre></code>
2022-05-25 19:38:30 +02:00
<code><pre id="stdout"></pre></code>
2022-05-25 19:27:12 +02:00
</body>
<script>
async function sleep(time) {
return new Promise((resolve) => setTimeout(resolve, time));
}
async function formSubmit(e) {
e.preventDefault();
let response = { status: "sending first request..." };
const url = \`/api/requests?url=\${url_input.value}&\${domains.value
.split(",")
.map((d) => "domains[]=" + d)
.join("&")}\`;
const { id } = await (await fetch(url, { method: "post" })).json();
do {
response = await (await fetch(\`/api/requests/\${id}\`)).json();
console.log(response);
output.innerHTML = JSON.stringify(response, null, " ");
2022-05-25 19:38:30 +02:00
console.log(response.output);
stdout.innerHTML = response.output;
2022-05-25 19:27:12 +02:00
await sleep(1000);
} while (response.status !== "finished");
}
</script>
</html>`;
});
router.get("/preview", async (ctx) => {
2022-04-24 17:24:27 +02:00
const s = new Readable({ read() {} });
// stream data
ctx.response.set("content-type", "txt/html");
ctx.type = "html"; // <-- THIS is the important step!
ctx.body = s;
ctx.body.push("<!doctype html>");
2022-04-24 19:57:01 +02:00
const id = uuid();
ctx.body.push(
`<img id="preview" width="1080" height="608" src="/static/${id}/preview.png?id=0"/><br/>`
);
ctx.body.push(/* HTML */ `<script>
2022-04-24 20:35:24 +02:00
window.interval = setInterval(() => (preview.src = preview.src + "0"), 500);
2022-04-24 19:57:01 +02:00
</script>`);
2022-04-24 17:24:27 +02:00
const params = qs.parse(ctx.querystring);
s.push(`Got request to screenshot ${params.url}<pre>`);
let docker_id = "";
2022-04-24 18:58:08 +02:00
if (!params.url) {
ctx.body = "specify url!";
return;
}
2022-04-24 17:24:27 +02:00
const starter = spawn(
"docker",
[
2022-05-05 21:54:34 +02:00
...DOCKER_ARGS,
2022-04-24 18:58:54 +02:00
`{"url": "${params.url}", "third_party_domains": ["hotjar.com", "cookielaw.org", "facebook.com", "gemius.pl"]}`,
2022-04-24 17:24:27 +02:00
id,
],
{ cwd: process.cwd() }
);
starter.stdout.on("data", (data) => {
docker_id += data.toString().replace(/\n/g, "");
});
starter.on("close", () => {
ctx.body.push("spawned " + docker_id);
attach(docker_id, ctx.body);
});
});
2022-05-05 21:54:34 +02:00
router.post("/api/requests", async (ctx) => {
const params = qs.parse(ctx.querystring);
if (!params.url) {
ctx.body = "Specify url";
ctx.status = 422;
return;
}
if (!params.domains) {
ctx.body = "Specify domains";
ctx.status = 422;
return;
}
if (!Array.isArray(params.domains)) {
ctx.body = "'domains' should be an array of strings";
ctx.status = 422;
return;
}
const request = new ScreenshotRequest(params.url, params.domains);
ctx.status = 303;
ctx.redirect(`/api/requests/${request.id}`);
});
router.get("/api/requests/:id", async (ctx) => {
const request = requests[ctx.params.id];
if (!request) {
ctx.status = 404;
return;
}
ctx.body = await request.getJSON();
});
2022-04-24 17:24:27 +02:00
app.use(router.routes()).use(router.allowedMethods());
app.listen(3000);