cs2inspects
Version:
openskindb inspect sdk with bulk processing capabilities
233 lines (221 loc) • 8.07 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.workerManager = exports.WorkerManager = void 0;
const worker_threads_1 = require("worker_threads");
const events_1 = require("events");
const stats_1 = require("./stats");
class WorkerManager extends events_1.EventEmitter {
workers = new Map();
workerStats = new Map();
workerEnabled;
maxWorkers;
currentWorkerIndex = 0;
constructor(workerEnabled = false, maxWorkers = 4) {
super();
this.workerEnabled = workerEnabled;
this.maxWorkers = maxWorkers;
}
async init() {
if (!this.workerEnabled) {
return;
}
for (let i = 0; i < this.maxWorkers; i++) {
await this.createWorker(`worker-${i}`);
}
}
async createWorker(id) {
if (!this.workerEnabled) {
return;
}
try {
const workerScript = `
const { parentPort } = require('worker_threads');
const { request } = require('undici');
const API_BASE_URL = "https://inspect.openskindb.com/";
async function processInspection(inspectUrl) {
try {
const encodedUrl = encodeURIComponent(inspectUrl);
const apiUrl = \`\${API_BASE_URL}?url=\${encodedUrl}\`;
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 30000);
const { statusCode, body } = await request(apiUrl, {
method: "GET",
headers: {
"User-Agent": "cs2inspects/1.0.0",
"Accept": "application/json",
},
signal: controller.signal,
});
clearTimeout(timeoutId);
const data = await body.json();
if (statusCode === 200 && data && data.iteminfo) {
return { success: true, data };
}
return {
success: false,
error: data?.message || data?.error || \`Request failed with status \${statusCode}\`
};
} catch (error) {
return {
success: false,
error: error.message || "Unknown error occurred"
};
}
}
parentPort.on('message', async (message) => {
const { type, data, id } = message;
try {
switch (type) {
case 'inspect':
const result = await processInspection(data.url);
parentPort.postMessage({
type: 'result',
data: result,
id
});
break;
case 'shutdown':
process.exit(0);
break;
default:
parentPort.postMessage({
type: 'error',
error: \`Unknown message type: \${type}\`,
id
});
}
} catch (error) {
parentPort.postMessage({
type: 'error',
error: error.message || "Worker error",
id
});
}
});
parentPort.postMessage({ type: 'ready' });
`;
const worker = new worker_threads_1.Worker(workerScript, { eval: true });
const stats = {
id,
active: true,
processed: 0,
errors: 0,
lastActivity: Date.now(),
};
worker.on("message", (response) => {
this.handleWorkerMessage(id, response);
});
worker.on("error", (error) => {
console.error(`Worker ${id} error:`, error);
stats.errors++;
stats.active = false;
this.emit("workerError", { id, error });
});
worker.on("exit", (code) => {
if (code !== 0) {
console.error(`Worker ${id} exited with code ${code}`);
}
stats.active = false;
this.workers.delete(id);
this.workerStats.delete(id);
stats_1.statsManager.removeWorker(id);
});
this.workers.set(id, worker);
this.workerStats.set(id, stats);
stats_1.statsManager.addWorker(id);
}
catch (error) {
console.error(`Failed to create worker ${id}:`, error);
}
}
handleWorkerMessage(workerId, response) {
const stats = this.workerStats.get(workerId);
if (stats) {
stats.lastActivity = Date.now();
if (response.type === "result") {
stats.processed++;
stats_1.statsManager.updateWorker(workerId, true, false);
}
else if (response.type === "error") {
stats.errors++;
stats_1.statsManager.updateWorker(workerId, false, true);
}
}
this.emit("workerMessage", { workerId, response });
}
async processInWorker(inspectUrl) {
if (!this.workerEnabled || this.workers.size === 0) {
throw new Error("Workers not available");
}
return new Promise((resolve, reject) => {
const workerId = this.getNextWorker();
const worker = this.workers.get(workerId);
if (!worker) {
reject(new Error("No available workers"));
return;
}
const messageId = `${Date.now()}-${Math.random()
.toString(36)
.substr(2, 9)}`;
const timeout = setTimeout(() => {
reject(new Error("Worker timeout"));
}, 35000);
const messageHandler = (response) => {
if (response.id === messageId) {
clearTimeout(timeout);
this.off("workerMessage", messageHandler);
if (response.type === "result") {
resolve(response.data);
}
else {
reject(new Error(response.error || "Worker error"));
}
}
};
this.on("workerMessage", messageHandler);
worker.postMessage({
type: "inspect",
data: { url: inspectUrl },
id: messageId,
});
});
}
getNextWorker() {
const workerIds = Array.from(this.workers.keys());
if (workerIds.length === 0) {
throw new Error("No workers available");
}
const workerId = workerIds[this.currentWorkerIndex];
this.currentWorkerIndex = (this.currentWorkerIndex + 1) % workerIds.length;
return workerId;
}
getWorkerStats() {
return Array.from(this.workerStats.values());
}
getActiveWorkerCount() {
return Array.from(this.workerStats.values()).filter((s) => s.active).length;
}
async shutdown() {
for (const [id, worker] of this.workers) {
worker.postMessage({ type: "shutdown", data: null, id: "shutdown" });
}
await new Promise((resolve) => setTimeout(resolve, 1000));
for (const [id, worker] of this.workers) {
await worker.terminate();
}
this.workers.clear();
this.workerStats.clear();
}
isEnabled() {
return this.workerEnabled;
}
enable() {
this.workerEnabled = true;
}
disable() {
this.workerEnabled = false;
}
}
exports.WorkerManager = WorkerManager;
exports.workerManager = new WorkerManager(process.env.WORKER_ENABLED === "true", parseInt(process.env.MAX_WORKERS || "4", 10));
//# sourceMappingURL=worker.js.map