Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0db0c656b2 | ||
|
|
d48254fa57 | ||
|
|
b2fdab0418 | ||
|
|
e78dc86aa3 | ||
|
|
e70d804ab8 | ||
|
|
b7c3a48377 |
No files matched your search
@@ -217,7 +217,7 @@ export const integrationDefs = {
|
|||||||
name: "Nextcloud",
|
name: "Nextcloud",
|
||||||
secretKinds: [["username", "password"]],
|
secretKinds: [["username", "password"]],
|
||||||
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/nextcloud.svg",
|
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/nextcloud.svg",
|
||||||
category: ["calendar"],
|
category: ["calendar", "notifications"],
|
||||||
documentationUrl: createDocumentationLink("/docs/integrations/nextcloud"),
|
documentationUrl: createDocumentationLink("/docs/integrations/nextcloud"),
|
||||||
},
|
},
|
||||||
unifiController: {
|
unifiController: {
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ import utc from "dayjs/plugin/utc";
|
|||||||
import type { RequestInit as NodeFetchRequestInit } from "node-fetch";
|
import type { RequestInit as NodeFetchRequestInit } from "node-fetch";
|
||||||
import * as ical from "node-ical";
|
import * as ical from "node-ical";
|
||||||
import { DAVClient } from "tsdav";
|
import { DAVClient } from "tsdav";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
import { createHttpsAgentAsync } from "@homarr/core/infrastructure/http";
|
import { ResponseError } from "@homarr/common/server";
|
||||||
|
import { createHttpsAgentAsync, fetchWithTrustedCertificatesAsync } from "@homarr/core/infrastructure/http";
|
||||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
import { createLogger } from "@homarr/core/infrastructure/logs";
|
||||||
|
|
||||||
import { HandleIntegrationErrors } from "../base/errors/decorator";
|
import { HandleIntegrationErrors } from "../base/errors/decorator";
|
||||||
@@ -16,6 +18,21 @@ import { Integration } from "../base/integration";
|
|||||||
import type { TestingResult } from "../base/test-connection/test-connection-service";
|
import type { TestingResult } from "../base/test-connection/test-connection-service";
|
||||||
import type { ICalendarIntegration } from "../interfaces/calendar/calendar-integration";
|
import type { ICalendarIntegration } from "../interfaces/calendar/calendar-integration";
|
||||||
import type { CalendarEvent } from "../interfaces/calendar/calendar-types";
|
import type { CalendarEvent } from "../interfaces/calendar/calendar-types";
|
||||||
|
import type { Notification } from "../interfaces/notifications/notification-types";
|
||||||
|
import type { INotificationsIntegration } from "../interfaces/notifications/notifications-integration";
|
||||||
|
|
||||||
|
const notificationSchema = z.object({
|
||||||
|
ocs: z.object({
|
||||||
|
data: z.array(
|
||||||
|
z.object({
|
||||||
|
notification_id: z.number(),
|
||||||
|
datetime: z.string(),
|
||||||
|
subject: z.string(),
|
||||||
|
message: z.string(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
const logger = createLogger({ module: "nextcloudIntegration" });
|
const logger = createLogger({ module: "nextcloudIntegration" });
|
||||||
|
|
||||||
@@ -23,7 +40,7 @@ dayjs.extend(utc);
|
|||||||
dayjs.extend(timezone);
|
dayjs.extend(timezone);
|
||||||
|
|
||||||
@HandleIntegrationErrors([integrationTsdavHttpErrorHandler])
|
@HandleIntegrationErrors([integrationTsdavHttpErrorHandler])
|
||||||
export class NextcloudIntegration extends Integration implements ICalendarIntegration {
|
export class NextcloudIntegration extends Integration implements ICalendarIntegration, INotificationsIntegration {
|
||||||
protected async testingAsync(input: IntegrationTestingInput): Promise<TestingResult> {
|
protected async testingAsync(input: IntegrationTestingInput): Promise<TestingResult> {
|
||||||
const client = await this.createCalendarClientAsync(await createHttpsAgentAsync(input.options));
|
const client = await this.createCalendarClientAsync(await createHttpsAgentAsync(input.options));
|
||||||
await client.login();
|
await client.login();
|
||||||
@@ -104,6 +121,29 @@ export class NextcloudIntegration extends Integration implements ICalendarIntegr
|
|||||||
.flat();
|
.flat();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getNotificationsAsync(): Promise<Notification[]> {
|
||||||
|
const url = this.url("/ocs/v2.php/apps/notifications/api/v2/notifications", { format: "json" });
|
||||||
|
const response = await fetchWithTrustedCertificatesAsync(url, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Basic ${Buffer.from(
|
||||||
|
`${this.getSecretValue("username")}:${this.getSecretValue("password")}`,
|
||||||
|
).toString("base64")}`,
|
||||||
|
"OCS-APIRequest": "true",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) throw new ResponseError(response);
|
||||||
|
|
||||||
|
const json = notificationSchema.parse(await response.json());
|
||||||
|
|
||||||
|
return json.ocs.data.map((notification) => ({
|
||||||
|
id: String(notification.notification_id),
|
||||||
|
time: new Date(notification.datetime),
|
||||||
|
title: notification.subject,
|
||||||
|
body: notification.message,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
private async createCalendarClientAsync(agent?: Agent) {
|
private async createCalendarClientAsync(agent?: Agent) {
|
||||||
return new DAVClient({
|
return new DAVClient({
|
||||||
serverUrl: this.integration.url,
|
serverUrl: this.integration.url,
|
||||||
|
|||||||
@@ -57,10 +57,12 @@ export const AddBoardModal = createModal(({ actions }) => {
|
|||||||
{...form.getInputProps("name")}
|
{...form.getInputProps("name")}
|
||||||
description={
|
description={
|
||||||
boardNameStatus.description ? (
|
boardNameStatus.description ? (
|
||||||
<Group c={boardNameStatus.description.color} gap="xs" align="center">
|
<span>
|
||||||
{boardNameStatus.description.icon ? <boardNameStatus.description.icon size={16} /> : null}
|
<Group c={boardNameStatus.description.color} gap="xs" align="center">
|
||||||
<span>{boardNameStatus.description.label}</span>
|
{boardNameStatus.description.icon ? <boardNameStatus.description.icon size={16} /> : null}
|
||||||
</Group>
|
<span>{boardNameStatus.description.label}</span>
|
||||||
|
</Group>
|
||||||
|
</span>
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ const optionMapping: OptionMapping = {
|
|||||||
allowMicrophone: (oldOptions) => oldOptions.allowMicrophone,
|
allowMicrophone: (oldOptions) => oldOptions.allowMicrophone,
|
||||||
allowGeolocation: (oldOptions) => oldOptions.allowGeolocation,
|
allowGeolocation: (oldOptions) => oldOptions.allowGeolocation,
|
||||||
allowScrolling: (oldOptions) => oldOptions.allowScrolling,
|
allowScrolling: (oldOptions) => oldOptions.allowScrolling,
|
||||||
|
allowModals: () => undefined,
|
||||||
},
|
},
|
||||||
video: {
|
video: {
|
||||||
feedUrl: (oldOptions) => oldOptions.FeedUrl,
|
feedUrl: (oldOptions) => oldOptions.FeedUrl,
|
||||||
|
|||||||
@@ -119,4 +119,5 @@ const permissionMapping = {
|
|||||||
allowGeolocation: "geolocation",
|
allowGeolocation: "geolocation",
|
||||||
allowMicrophone: "microphone",
|
allowMicrophone: "microphone",
|
||||||
allowPayment: "payment",
|
allowPayment: "payment",
|
||||||
|
allowModals: "allow-modals",
|
||||||
} satisfies Record<keyof Omit<WidgetComponentProps<"iframe">["options"], "embedUrl" | "allowScrolling">, string>;
|
} satisfies Record<keyof Omit<WidgetComponentProps<"iframe">["options"], "embedUrl" | "allowScrolling">, string>;
|
||||||
Reference in new issue
Block a user