@naria2/node
Version:
Cross-platform wrapper of aria2
115 lines (112 loc) • 3.47 kB
JavaScript
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { promises } from 'node:fs';
import getPort from 'get-port';
async function launchWebUI(options) {
const serveStatic = interopDefaultCompat(await import('serve-static'));
const finalhandler = interopDefaultCompat(await import('finalhandler'));
const http = await import('http');
const host = options.host;
const port = await getPort({ host: options.host, port: options.port });
const clientDir = fileURLToPath(new URL("../client", import.meta.url));
const serve = serveStatic(clientDir, { index: ["index.html"] });
const handler = await createWebUIHandler(options);
const server = http.createServer(async (req, res) => {
if (await handler(req, res)) {
return;
}
if (req.url?.startsWith("/connect")) {
req.url = req.url.replace("/connect", "/");
}
serve(req, res, finalhandler(req, res));
});
server.listen(port, options.host);
return {
server,
host,
port,
url: `http://${host ?? "0.0.0.0"}:${port}?port=${options.rpc.port}${options.rpc.secret ? `&secret=${options.rpc.secret}` : ""}`
};
}
async function attachWebUI(_socket, options = {}) {
const socket = await _socket;
const { server, host, port, url } = await launchWebUI({
host: options.host,
port: options.port ?? socket.getOptions().listenPort + 1,
rpc: {
port: socket.getOptions().listenPort,
secret: socket.getOptions().secret
}
});
socket.onClose(() => {
server.close();
});
return {
socket,
server,
host,
port,
url
};
}
async function createWebUIHandler(options) {
const { createProxyMiddleware } = await import('http-proxy-middleware');
const proxyMiddleware = createProxyMiddleware({
target: `http://127.0.0.1:${options.rpc.port}`,
changeOrigin: false,
ws: true,
logger: void 0
});
return async (req, res) => {
if (!req.url)
return false;
try {
const url = new URL(req.url, `http://${req.headers.host}`);
if (url.pathname === "/jsonrpc") {
await proxyMiddleware(req, res, () => {
});
return true;
} else if (url.pathname === "/_/open") {
return await handleWebUIOpenRequest(url, req, res, options);
}
return false;
} catch (error) {
return false;
}
};
}
async function handleWebUIOpenRequest(url, req, res, options) {
try {
const auth = req.headers.authorization;
res.setHeader("Content-Type", "application/json");
if (options.rpc.secret && options.rpc.secret !== auth) {
res.statusCode = 400;
res.write(JSON.stringify({ status: "ERROR" }));
} else {
const dir = url.searchParams.get("dir");
if (dir) {
const open = interopDefaultCompat(await import('open'));
const p = decodeURIComponent(dir);
const stat = await promises.stat(p);
if (stat.isDirectory()) {
await open(p).catch(() => {
});
res.write(JSON.stringify({ status: "OK", open: p }));
} else {
const d = path.dirname(p);
await open(d).catch(() => {
});
res.write(JSON.stringify({ status: "OK", open: d }));
}
}
}
res.end();
return true;
} catch (error) {
return false;
}
}
function interopDefaultCompat(mod) {
return mod.default ?? mod;
}
export { attachWebUI, createWebUIHandler, handleWebUIOpenRequest, launchWebUI };