Initial commit of Vikunja widget
This commit is contained in:
parent
b1ca6b8e1a
commit
60c6123dc8
19
docs/widgets/services/vikunja.md
Normal file
19
docs/widgets/services/vikunja.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
title: Vikunja
|
||||||
|
description: Vikunja Widget Configuration
|
||||||
|
---
|
||||||
|
|
||||||
|
Learn more about [Vikunja](https://vikunja.io).
|
||||||
|
|
||||||
|
Allowed fields: `["projects", "tasks"]`.
|
||||||
|
|
||||||
|
"Projects" lists the number of non-archived Projects the user has access to.
|
||||||
|
|
||||||
|
"Tasks" lists the number of tasks due within the next 7 days.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
widget:
|
||||||
|
type: vikunja
|
||||||
|
url: http[s]://vikunja.host.or.ip[:port]
|
||||||
|
key: vikunjaapikey
|
||||||
|
```
|
||||||
@ -953,5 +953,9 @@
|
|||||||
"reminders": "Reminders",
|
"reminders": "Reminders",
|
||||||
"nextReminder": "Next Reminder",
|
"nextReminder": "Next Reminder",
|
||||||
"none": "None"
|
"none": "None"
|
||||||
|
},
|
||||||
|
"vikunja": {
|
||||||
|
"projects": "Total Active Projects",
|
||||||
|
"tasks": "Tasks Due This Week"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,6 +44,7 @@ export default async function credentialedProxyHandler(req, res, map) {
|
|||||||
"tailscale",
|
"tailscale",
|
||||||
"tandoor",
|
"tandoor",
|
||||||
"pterodactyl",
|
"pterodactyl",
|
||||||
|
"vikunja",
|
||||||
].includes(widget.type)
|
].includes(widget.type)
|
||||||
) {
|
) {
|
||||||
headers.Authorization = `Bearer ${widget.key}`;
|
headers.Authorization = `Bearer ${widget.key}`;
|
||||||
|
|||||||
@ -125,6 +125,7 @@ const components = {
|
|||||||
uptimekuma: dynamic(() => import("./uptimekuma/component")),
|
uptimekuma: dynamic(() => import("./uptimekuma/component")),
|
||||||
uptimerobot: dynamic(() => import("./uptimerobot/component")),
|
uptimerobot: dynamic(() => import("./uptimerobot/component")),
|
||||||
urbackup: dynamic(() => import("./urbackup/component")),
|
urbackup: dynamic(() => import("./urbackup/component")),
|
||||||
|
vikunja: dynamic(() => import("./vikunja/component")),
|
||||||
watchtower: dynamic(() => import("./watchtower/component")),
|
watchtower: dynamic(() => import("./watchtower/component")),
|
||||||
wgeasy: dynamic(() => import("./wgeasy/component")),
|
wgeasy: dynamic(() => import("./wgeasy/component")),
|
||||||
whatsupdocker: dynamic(() => import("./whatsupdocker/component")),
|
whatsupdocker: dynamic(() => import("./whatsupdocker/component")),
|
||||||
|
|||||||
37
src/widgets/vikunja/component.jsx
Normal file
37
src/widgets/vikunja/component.jsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
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: projectsData, error: projectsError } = useWidgetAPI(widget, "projects");
|
||||||
|
const { data: tasksData, error: tasksError } = useWidgetAPI(widget, "tasks", {
|
||||||
|
filter: "done=false&&due_date<=now+7d",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (projectsError || tasksError) {
|
||||||
|
const vikunjaError = projectsError ?? tasksError;
|
||||||
|
return <Container service={service} error={vikunjaError} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!projectsData || !tasksData) {
|
||||||
|
return (
|
||||||
|
<Container service={service}>
|
||||||
|
<Block label="vikunja.projects" />
|
||||||
|
<Block label="vikunja.tasks" />
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container service={service}>
|
||||||
|
<Block label="vikunja.projects" value={t("common.number", { value: projectsData.projects })} />
|
||||||
|
<Block label="vikunja.tasks" value={t("common.number", { value: tasksData.tasks })} />
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
49
src/widgets/vikunja/proxy.js
Normal file
49
src/widgets/vikunja/proxy.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { httpProxy } from "utils/proxy/http";
|
||||||
|
import { formatApiCall } from "utils/proxy/api-helpers";
|
||||||
|
import getServiceWidget from "utils/config/service-helpers";
|
||||||
|
import createLogger from "utils/logger";
|
||||||
|
import widgets from "widgets/widgets";
|
||||||
|
|
||||||
|
const proxyName = "vikunjaProxyHandler";
|
||||||
|
const logger = createLogger(proxyName);
|
||||||
|
|
||||||
|
export default async function vikunjaProxyHandler(req, res) {
|
||||||
|
const { group, service, endpoint } = req.query;
|
||||||
|
|
||||||
|
if (!group || !service) {
|
||||||
|
logger.error("Invalid or missing service '%s' or group '%s'", service, group);
|
||||||
|
return res.status(400).json({ error: "Invalid proxy service type" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const widget = await getServiceWidget(group, service);
|
||||||
|
if (!widget || !widgets[widget.type].api) {
|
||||||
|
logger.error("Invalid or missing widget for service '%s' in group '%s'", service, group);
|
||||||
|
return res.status(400).json({ error: "Invalid widget configuration" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget }));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: `Bearer ${widget.token}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
logger.debug("Calling Vikunja API endpoint: %s", endpoint);
|
||||||
|
|
||||||
|
const [status, , data] = await httpProxy(url, params);
|
||||||
|
|
||||||
|
if (status !== 200) {
|
||||||
|
logger.error("Error calling Vikunja API: %d. Data: %s", status, data);
|
||||||
|
return res.status(status).json({ error: "Vikunja API Error", data });
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(status).send(data);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Exception calling Vikunja API: %s", error.message);
|
||||||
|
return res.status(500).json({ error: "Vikunja API Error", message: error.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/widgets/vikunja/widget.js
Normal file
26
src/widgets/vikunja/widget.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
|
||||||
|
import { jsonArrayFilter } from "utils/proxy/api-helpers";
|
||||||
|
|
||||||
|
const widget = {
|
||||||
|
api: `{url}/api/v1/{endpoint}`,
|
||||||
|
proxyHandler: credentialedProxyHandler,
|
||||||
|
|
||||||
|
mappings: {
|
||||||
|
projects: {
|
||||||
|
endpoint: "projects",
|
||||||
|
map: (data) => ({
|
||||||
|
projects: jsonArrayFilter(data, (item) => !item.isArchived).length,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
tasks: {
|
||||||
|
endpoint: "tasks/all",
|
||||||
|
// to filter by done=false and dueDate <= now+7d or whatever
|
||||||
|
params: ["filter"],
|
||||||
|
map: (data) => ({
|
||||||
|
tasks: jsonArrayFilter(data, (item) => !item.done).length,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default widget;
|
||||||
@ -115,6 +115,7 @@ import unifi from "./unifi/widget";
|
|||||||
import unmanic from "./unmanic/widget";
|
import unmanic from "./unmanic/widget";
|
||||||
import uptimekuma from "./uptimekuma/widget";
|
import uptimekuma from "./uptimekuma/widget";
|
||||||
import uptimerobot from "./uptimerobot/widget";
|
import uptimerobot from "./uptimerobot/widget";
|
||||||
|
import vikunja from "./vikunja/widget";
|
||||||
import watchtower from "./watchtower/widget";
|
import watchtower from "./watchtower/widget";
|
||||||
import wgeasy from "./wgeasy/widget";
|
import wgeasy from "./wgeasy/widget";
|
||||||
import whatsupdocker from "./whatsupdocker/widget";
|
import whatsupdocker from "./whatsupdocker/widget";
|
||||||
@ -246,6 +247,7 @@ const widgets = {
|
|||||||
uptimekuma,
|
uptimekuma,
|
||||||
uptimerobot,
|
uptimerobot,
|
||||||
urbackup,
|
urbackup,
|
||||||
|
vikunja,
|
||||||
watchtower,
|
watchtower,
|
||||||
wgeasy,
|
wgeasy,
|
||||||
whatsupdocker,
|
whatsupdocker,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user