figura-tools/index.js

184 lines
5.2 KiB
JavaScript

import { websocket, http, authenication } from "figura";
import { createServer } from "http";
import { pack_image } from "./lib/pack.js";
import { imageToJson } from "./lib/image2json.js";
import "dotenv/config";
let [___, token] = await authenication.getToken();
if (!(await http.checkAuth(token))) {
await authenication.invalidateCache();
let [u, t] = await authenication.getToken();
uuid = u;
token = t;
}
const ws = new websocket.WebsocketClient("wss://" + http.host + "/ws");
ws.addListener("connect", () => {
console.log("[WS] WS connection made");
});
ws.addListener("auth", () => {
console.log("[WS] WS successfully authenticated!");
["21f7143a-45cd-4995-b1e3-6c3c8602ef7c"].forEach((z) => {
ws.sub(z);
});
});
function sendChunkedResponse(url, status, body) {
if (body.length <= 600) {
ws.ping(true, "httpResponse", [{ url, status, body }]);
} else {
const parts = [];
for (let i = 0; i < body.length; i += 600) {
parts.push(body.slice(i, i + 600));
}
const amount = parts.length;
ws.ping(true, "httpResponse", [{ url, status, body_amount: amount }]);
parts.forEach((chunk, index) => {
setTimeout(
() => {
ws.ping(true, "httpResponse", [
{ url, status, body_id: index + 1, body_text: chunk },
]);
},
1600 * (index + 1),
);
});
}
}
function sendChunkedImageResponse(url, status, imageBuffer) {
const CHUNK_SIZE = 600;
if (status !== 200 || !imageBuffer) {
ws.ping(true, "imageResponse", [{ url, status }]);
return;
}
const totalLength = imageBuffer.length || imageBuffer.byteLength;
if (totalLength <= CHUNK_SIZE) {
ws.ping(true, "imageResponse", [
{ url, status, image: Buffer.from(imageBuffer).toString("base64") },
]);
} else {
const parts = [];
for (let i = 0; i < totalLength; i += CHUNK_SIZE) {
const chunk = Buffer.from(imageBuffer).slice(i, i + CHUNK_SIZE);
parts.push(chunk.toString("base64"));
}
const amount = parts.length;
ws.ping(true, "imageResponse", [{ url, status, image_amount: amount }]);
parts.forEach((chunk, index) => {
setTimeout(
() => {
ws.ping(true, "imageResponse", [
{ url, status, image_id: index + 1, image_chunk: chunk },
]);
},
1600 * (index + 1),
);
});
}
}
ws.addListener("ping", async (id, uuid, args) => {
try {
if (id === http.hashFiguraString("httpGet")) {
const url = args[0];
const res = await fetch(url);
const text = await res.text();
sendChunkedResponse(url, res.status, text);
}
if (id === http.hashFiguraString("httpPost")) {
const [url, payload] = args;
const res = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
const text = await res.text();
sendChunkedResponse(url, res.status, text);
}
} catch (err) {
ws.ping(true, "httpResponse", [{ error: err.message }]);
}
try {
if (id === http.hashFiguraString("evilAssImageGet")) {
let [url, width, height] = args;
const res = await fetch(url);
if (!res.ok) {
ws.ping(true, "imageResponse", [{ url, status: res.status }]);
return;
}
const blob = await res.blob();
const arrayBuffer = await blob.arrayBuffer();
const imageJson = await imageToJson(new Uint8Array(arrayBuffer), {
width,
height,
keepRatio: false,
});
const packedImage = pack_image(imageJson.json);
sendChunkedImageResponse(url, 200, packedImage.toString("utf8"));
}
} catch (err) {
ws.ping(true, "imageResponse", [{ error: err.toString().slice(0, 256) }]);
}
});
ws.connect();
const server = createServer(async (req, res) => {
if (req.method === "POST" && req.url === "/data") {
const authHeader = req.headers["authorization"];
if (!authHeader || !authHeader.startsWith("Bearer ")) {
res.writeHead(401, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "Missing or invalid auth header" }));
return;
}
const token = authHeader.substring("Bearer ".length).trim();
if (token !== process.env.PASSWORD) {
res.writeHead(403, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "Forbidden" }));
return;
}
let body = "";
req.on("data", (chunk) => (body += chunk));
req.on("end", () => {
try {
const json = JSON.parse(body);
ws.ping(true, "data", [json]);
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ ok: true }));
} catch {
res.writeHead(400, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "Invalid JSON" }));
}
});
} else {
res.writeHead(404, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "Not found" }));
}
});
server.listen(+process.env.PORT, process.env.HOST, () => {
console.log(
`[HTTP] Listening on http://${process.env.HOST}:${process.env.PORT} (POST /data)`,
);
console.log(
`[HTTP] Use header: Authorization: Bearer ${process.env.PASSWORD}`,
);
});