screenshot-service/src/request.ts
2022-06-19 13:33:25 +02:00

140 lines
3.8 KiB
TypeScript

import { is, predicates } from "@sealcode/ts-predicates";
import { ChildProcessWithoutNullStreams, spawn } from "child_process";
import { v4 as uuid } from "uuid";
import containerPool from "./container-pool";
import { q, requests } from "./memory";
let queue_order: ScreenshotRequest[] = [];
export default class ScreenshotRequest {
public id = uuid();
public status = "waiting";
public output = "";
public images: Record<string, unknown>[] = [];
public request_time: number = Date.now();
public started_time: number | null = null;
public finished_time: number | null = null;
public processing_took: number | null = null;
public waiting_took: number | null = null;
public process: ChildProcessWithoutNullStreams;
constructor(public url: string, public domains: string[]) {
q.push(async () => {
return this.exec();
});
requests[this.id] = this;
queue_order.push(this);
}
getJobsAhead(): number {
if (this.status != "waiting") {
return 0;
}
let count = 0;
for (const request of queue_order) {
if (request == this) {
break;
}
count++;
}
return count;
}
async getJSON(): Promise<{
url: string;
domains: string[];
jobs_ahead: number;
id: string;
status: string;
output: string;
images: Record<string, unknown>[];
request_time: number;
started_time: number | null;
finished_time: number | null;
processing_took: number | null;
waiting_took: number | null;
elapsed_time_s: number;
}> {
return {
url: this.url,
domains: this.domains,
jobs_ahead: this.getJobsAhead(),
id: this.id,
status: this.status,
output: this.output,
images: this.images,
request_time: this.request_time,
started_time: this.started_time,
finished_time: this.finished_time,
processing_took: this.processing_took,
waiting_took: this.waiting_took,
elapsed_time_s: Math.round(
((this.status === "finished" ? this.finished_time || -1 : Date.now()) -
this.request_time) /
1000
),
};
}
setFinished(): void {
this.status = "finished";
this.finished_time = Date.now();
if (this.started_time) {
this.processing_took = this.finished_time - this.started_time;
this.waiting_took = this.started_time - this.request_time;
}
}
async exec(): Promise<void> {
this.started_time = Date.now();
this.status = "running";
const container = containerPool.getContainer();
await container.waitReady();
return new Promise((resolve, reject) => {
this.process = spawn(
"docker",
[
"exec",
container.id,
"/opt/run-analysis.sh",
JSON.stringify({
url: this.url,
third_party_domains: this.domains,
}),
this.id,
],
{ cwd: process.cwd() }
);
this.process.on("close", (exitCode) => {
this.setFinished();
container.close();
queue_order = queue_order.filter((request) => request != this);
if (exitCode === 0) {
resolve();
} else {
reject();
}
});
this.process.stdout.on("data", (d: Buffer) => {
try {
const parsed = JSON.parse(d.toString()) as unknown;
if (
is(parsed, predicates.object) &&
is(parsed.new_file, predicates.object)
) {
this.images.push(parsed.new_file);
}
} catch (e) {
//noop
}
this.output += d.toString();
/* console.log("DATA!", d.toString()); */
});
this.process.stderr.on("data", (d: Buffer) => {
this.output += d.toString();
/* console.log("STDERR!", d.toString()); */
});
});
}
}