mirror of
https://github.com/karl0ss/homepage.git
synced 2025-04-29 12:03:41 +01:00
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>
This commit is contained in:
parent
2f4d4e52be
commit
992516cebd
@ -34,6 +34,7 @@
|
||||
"swr": "^1.3.0",
|
||||
"systeminformation": "^5.17.12",
|
||||
"tough-cookie": "^4.1.2",
|
||||
"urbackup-server-api": "^0.8.9",
|
||||
"winston": "^3.8.2",
|
||||
"xml-js": "^1.6.11"
|
||||
},
|
||||
|
45
pnpm-lock.yaml
generated
45
pnpm-lock.yaml
generated
@ -73,6 +73,9 @@ dependencies:
|
||||
tough-cookie:
|
||||
specifier: ^4.1.2
|
||||
version: 4.1.2
|
||||
urbackup-server-api:
|
||||
specifier: ^0.8.9
|
||||
version: 0.8.9
|
||||
winston:
|
||||
specifier: ^3.8.2
|
||||
version: 3.8.2
|
||||
@ -659,6 +662,12 @@ packages:
|
||||
resolution: {integrity: sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==}
|
||||
dev: true
|
||||
|
||||
/async-mutex@0.3.2:
|
||||
resolution: {integrity: sha512-HuTK7E7MT7jZEh1P9GtRW9+aTWiDWWi9InbZ5hjxrnRa39KS4BW04+xLBhYNS2aXhHUIKZSw3gj4Pn1pj+qGAA==}
|
||||
dependencies:
|
||||
tslib: 2.5.0
|
||||
dev: false
|
||||
|
||||
/async@3.2.4:
|
||||
resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==}
|
||||
dev: false
|
||||
@ -2691,6 +2700,18 @@ packages:
|
||||
- babel-plugin-macros
|
||||
dev: false
|
||||
|
||||
/node-fetch@2.6.12:
|
||||
resolution: {integrity: sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==}
|
||||
engines: {node: 4.x || >=6.0.0}
|
||||
peerDependencies:
|
||||
encoding: ^0.1.0
|
||||
peerDependenciesMeta:
|
||||
encoding:
|
||||
optional: true
|
||||
dependencies:
|
||||
whatwg-url: 5.0.0
|
||||
dev: false
|
||||
|
||||
/node-int64@0.4.0:
|
||||
resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
|
||||
dev: false
|
||||
@ -3719,6 +3740,10 @@ packages:
|
||||
url-parse: 1.5.10
|
||||
dev: false
|
||||
|
||||
/tr46@0.0.3:
|
||||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||
dev: false
|
||||
|
||||
/triple-beam@1.3.0:
|
||||
resolution: {integrity: sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==}
|
||||
dev: false
|
||||
@ -3822,6 +3847,15 @@ packages:
|
||||
picocolors: 1.0.0
|
||||
dev: true
|
||||
|
||||
/urbackup-server-api@0.8.9:
|
||||
resolution: {integrity: sha512-Igu6A0xSZeMsiN6PWT7zG4aD+iJR5fXT/j5+xwAvnD/vCNfvVrettIsXv6MftxOajvTmtlgaYu8KDoH1EJQ6DQ==}
|
||||
dependencies:
|
||||
async-mutex: 0.3.2
|
||||
node-fetch: 2.6.12
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
dev: false
|
||||
|
||||
/uri-js@4.4.1:
|
||||
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
||||
dependencies:
|
||||
@ -3867,6 +3901,17 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: false
|
||||
|
||||
/webidl-conversions@3.0.1:
|
||||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||
dev: false
|
||||
|
||||
/whatwg-url@5.0.0:
|
||||
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
||||
dependencies:
|
||||
tr46: 0.0.3
|
||||
webidl-conversions: 3.0.1
|
||||
dev: false
|
||||
|
||||
/which-boxed-primitive@1.0.2:
|
||||
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
|
||||
dependencies:
|
||||
|
@ -686,5 +686,11 @@
|
||||
"maxPlayers": "Max players",
|
||||
"bots": "Bots",
|
||||
"ping": "Ping"
|
||||
},
|
||||
"urbackup": {
|
||||
"ok" : "Ok",
|
||||
"errored": "Errors",
|
||||
"noRecent": "Out of Date",
|
||||
"totalUsed": "Used Storage"
|
||||
}
|
||||
}
|
||||
|
@ -90,6 +90,7 @@ const components = {
|
||||
unifi: dynamic(() => import("./unifi/component")),
|
||||
unmanic: dynamic(() => import("./unmanic/component")),
|
||||
uptimekuma: dynamic(() => import("./uptimekuma/component")),
|
||||
urbackup: dynamic(() => import("./urbackup/component")),
|
||||
watchtower: dynamic(() => import("./watchtower/component")),
|
||||
whatsupdocker: dynamic(() => import("./whatsupdocker/component")),
|
||||
xteve: dynamic(() => import("./xteve/component")),
|
||||
|
93
src/widgets/urbackup/component.jsx
Normal file
93
src/widgets/urbackup/component.jsx
Normal file
@ -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>
|
||||
);
|
||||
}
|
33
src/widgets/urbackup/proxy.js
Normal file
33
src/widgets/urbackup/proxy.js
Normal file
@ -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" })
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
}
|
7
src/widgets/urbackup/widget.js
Normal file
7
src/widgets/urbackup/widget.js
Normal file
@ -0,0 +1,7 @@
|
||||
import urbackupProxyHandler from "./proxy";
|
||||
|
||||
const widget = {
|
||||
proxyHandler: urbackupProxyHandler,
|
||||
};
|
||||
|
||||
export default widget;
|
@ -87,6 +87,7 @@ import uptimekuma from "./uptimekuma/widget";
|
||||
import watchtower from "./watchtower/widget";
|
||||
import whatsupdocker from "./whatsupdocker/widget";
|
||||
import xteve from "./xteve/widget";
|
||||
import urbackup from "./urbackup/widget";
|
||||
|
||||
const widgets = {
|
||||
adguard,
|
||||
@ -177,6 +178,7 @@ const widgets = {
|
||||
unifi_console: unifi,
|
||||
unmanic,
|
||||
uptimekuma,
|
||||
urbackup,
|
||||
watchtower,
|
||||
whatsupdocker,
|
||||
xteve,
|
||||
|
Loading…
x
Reference in New Issue
Block a user