Widget for displaying scripted information
This widget executes a script and displays the script's output. The executed script must return the data in json format. The returned fields can be filtered, labeled and formatted.
This commit is contained in:
parent
c14ae6ee72
commit
40de43b2da
@ -298,5 +298,9 @@
|
||||
"rejectedPushes": "Rejected",
|
||||
"filters": "Filters",
|
||||
"indexers": "Indexers"
|
||||
},
|
||||
"scripted": {
|
||||
"yes": "Yes",
|
||||
"no": "No"
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,6 +30,7 @@ const components = {
|
||||
readarr: dynamic(() => import("./readarr/component")),
|
||||
rutorrent: dynamic(() => import("./rutorrent/component")),
|
||||
sabnzbd: dynamic(() => import("./sabnzbd/component")),
|
||||
scripted: dynamic(() => import("./scripted/component")),
|
||||
sonarr: dynamic(() => import("./sonarr/component")),
|
||||
speedtest: dynamic(() => import("./speedtest/component")),
|
||||
strelaysrv: dynamic(() => import("./strelaysrv/component")),
|
||||
|
||||
50
src/widgets/scripted/README.md
Normal file
50
src/widgets/scripted/README.md
Normal file
@ -0,0 +1,50 @@
|
||||
# Widget for displaying scripted information
|
||||
|
||||
This widget executes a script and displays the script's output. The executed script must return
|
||||
the data in json format. The returned fields can be filtered, labeled and formatted.
|
||||
|
||||
For example to display the online status and number of players of a minecraft server the
|
||||
configuration could be:
|
||||
|
||||
```yaml
|
||||
widget:
|
||||
type: scripted
|
||||
script: "mcstatus myserver:25565 json"
|
||||
fields: [ "online", "player_count", "ping" ]
|
||||
field_labels:
|
||||
player_count: "players"
|
||||
field_types:
|
||||
online: boolean
|
||||
ping:
|
||||
type: common.ms
|
||||
style: unit
|
||||
unit: millisecond
|
||||
unitDisplay: narrow
|
||||
```
|
||||
|
||||
The output of the executed script of the above example is
|
||||
|
||||
```json
|
||||
{ "online": true, "version": "1.19.2", "protocol": 760, "motd": "A Minecraft server", "player_count": 0, "player_max": 20, "players": [], "ping": 0.527 }
|
||||
```
|
||||
|
||||
From the scripts output the three fields <code>online</code>, <code>player_count</code> and <code>ping</code>
|
||||
will be displayed. The field <code>player_count</code> will be named "players". The fields <code>online</code>
|
||||
and <code>ping</code> will be formatted.
|
||||
|
||||
## Configuration
|
||||
|
||||
* **script** is the script that will be executed as the user that runs the homepage server.
|
||||
It's output must be in JSON format.
|
||||
|
||||
* **fields** names the fields from the script's output that shall be displayed.
|
||||
It is recommended to set the fields. Otherwise the widget will be empty if no data can be displayed.
|
||||
|
||||
* **field_labels** defines the labels to be displayed for the fields. The field name itself is used if there
|
||||
is no label for a field, like for the fields <code>online</code> and <code>ping</code> in the example.
|
||||
|
||||
* **field_types** defines the types of the fields. If unset then the value is shown unformatted.
|
||||
The type's value can either be a simple string like <code>common.number</code> or a map if multiple
|
||||
configuration options shall be used like in the example above for the <code>ping</code> field.
|
||||
See the "common.XY" translation strings in the file <code>public/locales/en/common.json</code> for
|
||||
supported field types. In addition the type <code>boolean</code> is supported.
|
||||
49
src/widgets/scripted/component.jsx
Normal file
49
src/widgets/scripted/component.jsx
Normal file
@ -0,0 +1,49 @@
|
||||
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";
|
||||
|
||||
export default function Component({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { widget } = service;
|
||||
|
||||
const { data: scriptedData, error: scriptedError } = useWidgetAPI(widget, '');
|
||||
|
||||
if (scriptedError) {
|
||||
return <Container error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
if (!scriptedData) {
|
||||
return (
|
||||
<Container service={service}>
|
||||
{widget.fields?.map(field => (
|
||||
<Block key={field} label={field} />
|
||||
))}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
(scriptedData || []).forEach(e => {
|
||||
if (e.type && e.value !== undefined) {
|
||||
if (typeof e.type === 'object') {
|
||||
e.typedValue = t(e.type.type, {...e.type, type: null, value: e.value});
|
||||
} else if (e.type == 'boolean') {
|
||||
e.typedValue = t(e.value ? 'scripted.yes' : 'scripted.no');
|
||||
} else {
|
||||
e.typedValue = t(e.type, { value: e.value });
|
||||
}
|
||||
} else {
|
||||
e.typedValue = e.value;
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="relative flex flex-row w-full">
|
||||
{(scriptedData || []).map(e => (
|
||||
<Block key={e.name} label={e.label} value={e.typedValue} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
36
src/widgets/scripted/proxy.js
Normal file
36
src/widgets/scripted/proxy.js
Normal file
@ -0,0 +1,36 @@
|
||||
import getServiceWidget from "utils/config/service-helpers";
|
||||
import createLogger from "utils/logger";
|
||||
|
||||
const logger = createLogger("scriptedProxyHandler");
|
||||
const { execSync } = require('child_process')
|
||||
|
||||
export default async function scriptedProxyHandler(req, res, map) {
|
||||
const { group, service, endpoint } = req.query;
|
||||
|
||||
if (group && service) {
|
||||
const widget = await getServiceWidget(group, service);
|
||||
|
||||
let output;
|
||||
try {
|
||||
output = JSON.parse(execSync(widget.script).toString());
|
||||
}
|
||||
catch (err) {
|
||||
return res.status(500).send('script failed: ' + err);
|
||||
}
|
||||
|
||||
const fields = widget.fields || Object.keys(output) || [];
|
||||
const labels = widget.field_labels || [];
|
||||
const types = widget.field_types || [];
|
||||
|
||||
const result = fields.map(field => {
|
||||
return {
|
||||
name: field, label: labels[field] || field, value: output[field], type: types[field]
|
||||
};
|
||||
});
|
||||
|
||||
return res.status(200).json(result);
|
||||
}
|
||||
|
||||
logger.debug("Invalid or missing proxy service type '%s' in group '%s'", service, group);
|
||||
return res.status(400).json({ error: "Invalid proxy service type" });
|
||||
}
|
||||
7
src/widgets/scripted/widget.js
Normal file
7
src/widgets/scripted/widget.js
Normal file
@ -0,0 +1,7 @@
|
||||
import scriptedProxyHandler from "./proxy";
|
||||
|
||||
const widget = {
|
||||
proxyHandler: scriptedProxyHandler,
|
||||
};
|
||||
|
||||
export default widget;
|
||||
@ -25,6 +25,7 @@ import radarr from "./radarr/widget";
|
||||
import readarr from "./readarr/widget";
|
||||
import rutorrent from "./rutorrent/widget";
|
||||
import sabnzbd from "./sabnzbd/widget";
|
||||
import scripted from './scripted/widget';
|
||||
import sonarr from "./sonarr/widget";
|
||||
import speedtest from "./speedtest/widget";
|
||||
import strelaysrv from "./strelaysrv/widget";
|
||||
@ -62,6 +63,7 @@ const widgets = {
|
||||
readarr,
|
||||
rutorrent,
|
||||
sabnzbd,
|
||||
scripted,
|
||||
sonarr,
|
||||
speedtest,
|
||||
strelaysrv,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user