Feature: UrBackup Widget (#1735)
* Add initial UrBackup widget with counts of ok, errored, and out-of date clients * Add configurable number of days since last backup before a client is considered out-of-date * Don't count a lack of recent (or error free) image backup if image backup isn't supported. * Add support for reporting total disk usage * add support for "fields" from services.yaml * fix field filtering, syntax * Consolidate urbackup code, syntax changes * Revert pnpm changes * re-add urbackup-server-api --------- Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>pull/1742/head
parent
2f4d4e52be
commit
992516cebd
@ -0,0 +1,93 @@
|
||||
import { useTranslation } from "next-i18next";
|
||||
|
||||
import Container from "components/services/widget/container";
|
||||
import Block from "components/services/widget/block";
|
||||
import useWidgetAPI from "utils/proxy/use-widget-api";
|
||||
|
||||
const Status = Object.freeze({
|
||||
ok: Symbol("Ok"),
|
||||
errored: Symbol("Errored"),
|
||||
noRecent: Symbol("No Recent Backups")
|
||||
});
|
||||
|
||||
function hasRecentBackups(client, maxDays){
|
||||
const days = maxDays || 3;
|
||||
const diffTime = days*24*60*60 // 7 days
|
||||
const recentFile = (client.lastbackup > (Date.now() / 1000 - diffTime));
|
||||
const recentImage = ((client.lastbackup_image > (Date.now() / 1000 - diffTime)||client.image_not_supported));
|
||||
return (recentFile && recentImage);
|
||||
}
|
||||
|
||||
function determineStatuses(urbackupData) {
|
||||
let ok = 0;
|
||||
let errored = 0;
|
||||
let noRecent = 0;
|
||||
let status;
|
||||
urbackupData.clientStatuses.forEach((client) => {
|
||||
status = Status.noRecent;
|
||||
if (hasRecentBackups(client, urbackupData.maxDays)) {
|
||||
status = (client.file_ok && (client.image_ok || client.image_not_supported)) ? Status.ok : Status.errored;
|
||||
}
|
||||
switch (status) {
|
||||
case Status.ok:
|
||||
ok += 1;
|
||||
break;
|
||||
case Status.errored:
|
||||
errored += 1;
|
||||
break;
|
||||
case Status.noRecent:
|
||||
noRecent += 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
let totalUsage = false;
|
||||
|
||||
// calculate total disk space if provided
|
||||
if (urbackupData.diskUsage) {
|
||||
totalUsage = 0.0;
|
||||
urbackupData.diskUsage.forEach((client) => {
|
||||
totalUsage += client.used;
|
||||
});
|
||||
}
|
||||
|
||||
return { ok, errored, noRecent, totalUsage };
|
||||
}
|
||||
|
||||
export default function Component({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { widget } = service;
|
||||
|
||||
const showDiskUsage = widget.fields?.includes('totalUsed')
|
||||
|
||||
const { data: urbackupData, error: urbackupError } = useWidgetAPI(widget, "status");
|
||||
|
||||
if (urbackupError) {
|
||||
return <Container service={service} error={urbackupError} />;
|
||||
}
|
||||
|
||||
if (!urbackupData) {
|
||||
return (
|
||||
<Container service={service}>
|
||||
<Block label="urbackup.ok" />
|
||||
<Block label="urbackup.errored" />
|
||||
<Block label="urbackup.noRecent" />
|
||||
{showDiskUsage && <Block label="urbackup.totalUsed" />}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
const statusData = determineStatuses(urbackupData, widget);
|
||||
|
||||
return (
|
||||
<Container service={service}>
|
||||
<Block label="urbackup.ok" value={t("common.number", { value: parseInt(statusData.ok, 10) })} />
|
||||
<Block label="urbackup.errored" value={t("common.number", { value: parseInt(statusData.errored, 10) })} />
|
||||
<Block label="urbackup.noRecent" value={t("common.number", { value: parseInt(statusData.noRecent, 10) })} />
|
||||
{showDiskUsage && <Block label="urbackup.totalUsed" value={t("common.bbytes", {value: parseFloat(statusData.totalUsage, 10)})} />}
|
||||
</Container>
|
||||
);
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
import {UrbackupServer} from "urbackup-server-api";
|
||||
|
||||
import getServiceWidget from "utils/config/service-helpers";
|
||||
|
||||
export default async function urbackupProxyHandler(req, res) {
|
||||
const {group, service} = req.query;
|
||||
const serviceWidget = await getServiceWidget(group, service);
|
||||
|
||||
const server = new UrbackupServer({
|
||||
url: serviceWidget.url,
|
||||
username: serviceWidget.username,
|
||||
password: serviceWidget.password
|
||||
});
|
||||
|
||||
await (async () => {
|
||||
try {
|
||||
const allClients = await server.getStatus({includeRemoved: false});
|
||||
let diskUsage = false
|
||||
if (serviceWidget.fields?.includes("totalUsed")) {
|
||||
diskUsage = await server.getUsage();
|
||||
}
|
||||
res.status(200).send({
|
||||
clientStatuses: allClients,
|
||||
diskUsage,
|
||||
maxDays: serviceWidget.maxDays
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: "Something Broke" })
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
import urbackupProxyHandler from "./proxy";
|
||||
|
||||
const widget = {
|
||||
proxyHandler: urbackupProxyHandler,
|
||||
};
|
||||
|
||||
export default widget;
|
Loading…
Reference in new issue