commit
b0f29709c2
@ -0,0 +1,16 @@
|
||||
import ServicesGetStatus from "./methods/services_get_status";
|
||||
import SmartGetList from "./methods/smart_get_list";
|
||||
import DownloaderGetDownloadList from "./methods/downloader_get_downloadlist";
|
||||
|
||||
export default function Component({ service }) {
|
||||
switch (service.widget.method) {
|
||||
case "services.getStatus":
|
||||
return <ServicesGetStatus service={service} />;
|
||||
case "smart.getListBg":
|
||||
return <SmartGetList service={service} />;
|
||||
case "downloader.getDownloadList":
|
||||
return <DownloaderGetDownloadList service={service} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
import useWidgetAPI from "utils/proxy/use-widget-api";
|
||||
import Container from "components/services/widget/container";
|
||||
import Block from "components/services/widget/block";
|
||||
|
||||
const downloadReduce = (acc, e) => {
|
||||
if (e.downloading) {
|
||||
return acc + 1;
|
||||
}
|
||||
return acc;
|
||||
};
|
||||
|
||||
const items = [
|
||||
{ label: "openmediavault.downloading", getNumber: (data) => (!data ? null : data.reduce(downloadReduce, 0)) },
|
||||
{ label: "openmediavault.total", getNumber: (data) => (!data ? null : data?.length) },
|
||||
];
|
||||
|
||||
export default function Component({ service }) {
|
||||
const { data, error } = useWidgetAPI(service.widget);
|
||||
|
||||
if (error) {
|
||||
return <Container service={service} error={error} />;
|
||||
}
|
||||
|
||||
const itemsWithData = items.map((item) => ({
|
||||
...item,
|
||||
number: item.getNumber(data?.response?.data),
|
||||
}));
|
||||
|
||||
return (
|
||||
<Container service={service}>
|
||||
{itemsWithData.map((e) => (
|
||||
<Block key={e.label} label={e.label} value={e.number} />
|
||||
))}
|
||||
</Container>
|
||||
);
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
import useWidgetAPI from "utils/proxy/use-widget-api";
|
||||
import Container from "components/services/widget/container";
|
||||
import Block from "components/services/widget/block";
|
||||
|
||||
const isRunningReduce = (acc, e) => {
|
||||
if (e.running) {
|
||||
return acc + 1;
|
||||
}
|
||||
return acc;
|
||||
};
|
||||
const notRunningReduce = (acc, e) => {
|
||||
if (!e.running) {
|
||||
return acc + 1;
|
||||
}
|
||||
return acc;
|
||||
};
|
||||
|
||||
const items = [
|
||||
{ label: "openmediavault.running", getNumber: (data) => (!data ? null : data.reduce(isRunningReduce, 0)) },
|
||||
{ label: "openmediavault.stopped", getNumber: (data) => (!data ? null : data.reduce(notRunningReduce, 0)) },
|
||||
{ label: "openmediavault.total", getNumber: (data) => (!data ? null : data?.length) },
|
||||
];
|
||||
|
||||
export default function Component({ service }) {
|
||||
const { data, error } = useWidgetAPI(service.widget);
|
||||
|
||||
if (error) {
|
||||
return <Container service={service} error={error} />;
|
||||
}
|
||||
|
||||
const itemsWithData = items.map((item) => ({
|
||||
...item,
|
||||
number: item.getNumber(data?.response?.data),
|
||||
}));
|
||||
|
||||
return (
|
||||
<Container service={service}>
|
||||
{itemsWithData.map((e) => (
|
||||
<Block key={e.label} label={e.label} value={e.number} />
|
||||
))}
|
||||
</Container>
|
||||
);
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
import useWidgetAPI from "utils/proxy/use-widget-api";
|
||||
import Container from "components/services/widget/container";
|
||||
import Block from "components/services/widget/block";
|
||||
|
||||
const passedReduce = (acc, e) => {
|
||||
if (e.overallstatus === "GOOD") {
|
||||
return acc + 1;
|
||||
}
|
||||
return acc;
|
||||
};
|
||||
const failedReduce = (acc, e) => {
|
||||
if (e.overallstatus !== "GOOD") {
|
||||
return acc + 1;
|
||||
}
|
||||
return acc;
|
||||
};
|
||||
|
||||
const items = [
|
||||
{ label: "openmediavault.passed", getNumber: (data) => (!data ? null : data.reduce(passedReduce, 0)) },
|
||||
{ label: "openmediavault.failed", getNumber: (data) => (!data ? null : data.reduce(failedReduce, 0)) },
|
||||
];
|
||||
|
||||
export default function Component({ service }) {
|
||||
const { data, error } = useWidgetAPI(service.widget);
|
||||
|
||||
if (error) {
|
||||
return <Container service={service} error={error} />;
|
||||
}
|
||||
|
||||
const itemsWithData = items.map((item) => ({
|
||||
...item,
|
||||
number: item.getNumber(JSON.parse(data?.response?.output || "{}")?.data),
|
||||
}));
|
||||
|
||||
return (
|
||||
<Container service={service}>
|
||||
{itemsWithData.map((e) => (
|
||||
<Block key={e.label} label={e.label} value={e.number} />
|
||||
))}
|
||||
</Container>
|
||||
);
|
||||
}
|
@ -0,0 +1,151 @@
|
||||
import { formatApiCall } from "utils/proxy/api-helpers";
|
||||
import { httpProxy } from "utils/proxy/http";
|
||||
import getServiceWidget from "utils/config/service-helpers";
|
||||
import { addCookieToJar, setCookieHeader } from "utils/proxy/cookie-jar";
|
||||
import createLogger from "utils/logger";
|
||||
import widgets from "widgets/widgets";
|
||||
|
||||
const PROXY_NAME = "OMVProxyHandler";
|
||||
const BG_MAX_RETRIES = 50;
|
||||
const BG_POLL_PERIOD = 500;
|
||||
|
||||
const logger = createLogger(PROXY_NAME);
|
||||
|
||||
async function getWidget(req) {
|
||||
const { group, service } = req.query;
|
||||
|
||||
if (!group || !service) {
|
||||
logger.debug("Invalid or missing service '%s' or group '%s'", service, group);
|
||||
return null;
|
||||
}
|
||||
|
||||
const widget = await getServiceWidget(group, service);
|
||||
|
||||
if (!widget) {
|
||||
logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group);
|
||||
return null;
|
||||
}
|
||||
|
||||
return widget;
|
||||
}
|
||||
|
||||
async function rpc(url, request) {
|
||||
const params = {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(request),
|
||||
};
|
||||
setCookieHeader(url, params);
|
||||
const [status, contentType, data, headers] = await httpProxy(url, params);
|
||||
|
||||
return { status, contentType, data, headers };
|
||||
}
|
||||
|
||||
async function poll(attemptsLeft, makeReqByPos, pos = 0) {
|
||||
if (attemptsLeft <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const resp = await makeReqByPos(pos);
|
||||
|
||||
const data = JSON.parse(resp.data.toString()).response;
|
||||
if (data.running === true || data.outputPending) {
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, BG_POLL_PERIOD);
|
||||
});
|
||||
return poll(attemptsLeft - 1, makeReqByPos, data.pos);
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
|
||||
async function tryLogin(widget) {
|
||||
const url = new URL(formatApiCall(widgets?.[widget.type]?.api, { ...widget }));
|
||||
const { username, password } = widget;
|
||||
const resp = await rpc(url, {
|
||||
method: "login",
|
||||
service: "session",
|
||||
params: { username, password },
|
||||
});
|
||||
|
||||
if (resp.status !== 200) {
|
||||
logger.error("HTTP %d logging in to OpenMediaVault. Data: %s", resp.status, resp.data);
|
||||
return [false, resp];
|
||||
}
|
||||
|
||||
const json = JSON.parse(resp.data.toString());
|
||||
if (json.response.authenticated !== true) {
|
||||
logger.error("Login error in OpenMediaVault. Data: %s", resp.data);
|
||||
resp.status = 401;
|
||||
return [false, resp];
|
||||
}
|
||||
|
||||
return [true, resp];
|
||||
}
|
||||
async function processBg(url, filename) {
|
||||
const resp = await poll(BG_MAX_RETRIES, (pos) =>
|
||||
rpc(url, {
|
||||
service: "exec",
|
||||
method: "getOutput",
|
||||
params: { pos, filename },
|
||||
})
|
||||
);
|
||||
|
||||
if (resp == null) {
|
||||
const errText = "The maximum number of attempts to receive a response from Bg data has been exceeded.";
|
||||
logger.error(errText);
|
||||
return errText;
|
||||
}
|
||||
if (resp.status !== 200) {
|
||||
logger.error("HTTP %d getting Bg data from OpenMediaVault RPC. Data: %s", resp.status, resp.data);
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
|
||||
export default async function proxyHandler(req, res) {
|
||||
const widget = await getWidget(req);
|
||||
if (!widget) {
|
||||
return res.status(400).json({ error: "Invalid proxy service type" });
|
||||
}
|
||||
|
||||
const api = widgets?.[widget.type]?.api;
|
||||
if (!api) {
|
||||
return res.status(403).json({ error: "Service does not support RPC calls" });
|
||||
}
|
||||
|
||||
const url = new URL(formatApiCall(api, { ...widget }));
|
||||
const [service, method] = widget.method.split(".");
|
||||
const rpcReq = { params: { limit: -1, start: 0 }, service, method };
|
||||
|
||||
let resp = await rpc(url, rpcReq);
|
||||
|
||||
if (resp.status === 401) {
|
||||
logger.debug("Session not authenticated.");
|
||||
const [success, lResp] = await tryLogin(widget);
|
||||
|
||||
if (success) {
|
||||
addCookieToJar(url, lResp.headers);
|
||||
} else {
|
||||
res.status(lResp.status).json({ error: { message: `HTTP Error ${lResp.status}`, url, data: lResp.data } });
|
||||
}
|
||||
|
||||
logger.debug("Retrying OpenMediaVault request after login.");
|
||||
resp = await rpc(url, rpcReq);
|
||||
}
|
||||
|
||||
if (resp.status !== 200) {
|
||||
logger.error("HTTP %d getting data from OpenMediaVault RPC. Data: %s", resp.status, resp.data);
|
||||
return res.status(resp.status).json({ error: { message: `HTTP Error ${resp.status}`, url, data: resp.data } });
|
||||
}
|
||||
|
||||
if (method.endsWith("Bg")) {
|
||||
const json = JSON.parse(resp.data.toString());
|
||||
const bgResp = await processBg(url, json.response);
|
||||
|
||||
if (typeof bgResp === "string") {
|
||||
return res.status(400).json({ error: bgResp });
|
||||
}
|
||||
return res.status(bgResp.status).send(bgResp.data);
|
||||
}
|
||||
|
||||
return res.status(resp.status).send(resp.data);
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
import proxyHandler from "./proxy";
|
||||
|
||||
const widget = {
|
||||
api: "{url}/rpc.php",
|
||||
proxyHandler,
|
||||
};
|
||||
|
||||
export default widget;
|
Loading…
Reference in new issue