184 lines
5.2 KiB
JavaScript
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: 20,
|
|
height: 20,
|
|
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}`,
|
|
);
|
|
});
|