server-core.js
Pfad: wetell-headless/server-core.js
Ext: js
Größe: 2925 Bytes
Geändert: 2026-07-16 17:51:21+02
'use strict';
const crypto = require('node:crypto');
function send(response, status, payload) {
const body = JSON.stringify(payload);
response.writeHead(status, {
'content-type': 'application/json; charset=utf-8',
'content-length': Buffer.byteLength(body),
'cache-control': 'no-store',
});
response.end(body);
}
function readJson(request, maximumBytes = 65536) {
return new Promise((resolve, reject) => {
const chunks = [];
let size = 0;
let failed = false;
request.on('data', (chunk) => {
if (failed) return;
size += chunk.length;
if (size > maximumBytes) {
failed = true;
reject(new Error('body_too_large'));
return;
}
chunks.push(chunk);
});
request.on('end', () => {
if (failed) return;
try { resolve(JSON.parse(Buffer.concat(chunks).toString('utf8'))); }
catch { reject(new Error('invalid_json')); }
});
request.on('error', reject);
});
}
function validSecret(actual, expected) {
if (typeof actual !== 'string') return false;
const left = Buffer.from(actual);
const right = Buffer.from(expected);
return left.length === right.length && crypto.timingSafeEqual(left, right);
}
function createRequestHandler(browser, secret, maximumActive = 2) {
if (!secret) throw new Error('secret_required');
let active = 0;
return async (request, response) => {
if (request.method === 'GET' && request.url === '/health') {
send(response, 200, { ok: true, ready: Boolean(browser.browser), active });
return;
}
if (request.method !== 'POST' || request.url !== '/') {
send(response, 405, { ok: false, error: 'method_not_allowed' });
return;
}
if (active >= maximumActive) {
send(response, 429, { ok: false, error: 'capacity_exceeded' });
return;
}
active++;
try {
const body = await readJson(request);
if (!body || !validSecret(body.secret, secret)) {
send(response, 403, { ok: false, error: 'forbidden' });
return;
}
const result = await browser.execute(String(body.action || ''), body);
send(response, 200, result);
} catch (error) {
const code = error instanceof Error ? error.message : 'runtime_error';
if (code === 'body_too_large') send(response, 413, { ok: false, error: code });
else if (code === 'invalid_json') send(response, 400, { ok: false, error: code });
else send(response, 502, { ok: false, error: 'upstream_failure' });
} finally {
active--;
}
};
}
module.exports = { createRequestHandler, readJson, validSecret };