Compare commits
34
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a0ae504ab | ||
|
|
177fa88998 | ||
|
|
5d9674412e | ||
|
|
e22096c7bc | ||
|
|
468c04da8a | ||
|
|
62c78f191c | ||
|
|
fa421b3778 | ||
|
|
1813d4435e | ||
|
|
e110a84fdd | ||
|
|
95be0391a6 | ||
|
|
6b474c3df4 | ||
|
|
ded39930ca | ||
|
|
86d4585035 | ||
|
|
4e892efab7 | ||
|
|
8ffbbd6bf9 | ||
|
|
17763208df | ||
|
|
07ce1c1a2d | ||
|
|
14c116cef4 | ||
|
|
7b8dc1670d | ||
|
|
da2d22b1b9 | ||
|
|
2c7f5de192 | ||
|
|
319bee99b7 | ||
|
|
4bf69874ec | ||
|
|
add73a1a3c | ||
|
|
3f0e9c3738 | ||
|
|
b8b71da291 | ||
|
|
f677bd6b12 | ||
|
|
e6f05f9d94 | ||
|
|
86608f5a9f | ||
|
|
f6039a5976 | ||
|
|
8ccbe7ff52 | ||
|
|
eede932884 | ||
|
|
a7052bb16f | ||
|
|
caedac490e |
No files matched your search
@@ -31,6 +31,7 @@ body:
|
|||||||
label: Version
|
label: Version
|
||||||
description: What version of Homarr are you running?
|
description: What version of Homarr are you running?
|
||||||
options:
|
options:
|
||||||
|
- 1.25.0
|
||||||
- 1.24.0
|
- 1.24.0
|
||||||
- 1.23.0
|
- 1.23.0
|
||||||
- 1.22.0
|
- 1.22.0
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
# https://github.com/webiny/action-conventional-commits?tab=readme-ov-file
|
|
||||||
name: "[Conventions] Semantic Commits"
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches: [ dev ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Conventional Commits
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: webiny/action-conventional-commits@v1.3.0
|
|
||||||
@@ -56,9 +56,9 @@
|
|||||||
"@mantine/tiptap": "^8.1.1",
|
"@mantine/tiptap": "^8.1.1",
|
||||||
"@million/lint": "1.0.14",
|
"@million/lint": "1.0.14",
|
||||||
"@tabler/icons-react": "^3.34.0",
|
"@tabler/icons-react": "^3.34.0",
|
||||||
"@tanstack/react-query": "^5.80.10",
|
"@tanstack/react-query": "^5.81.2",
|
||||||
"@tanstack/react-query-devtools": "^5.80.10",
|
"@tanstack/react-query-devtools": "^5.81.2",
|
||||||
"@tanstack/react-query-next-experimental": "^5.80.10",
|
"@tanstack/react-query-next-experimental": "^5.81.2",
|
||||||
"@trpc/client": "^11.4.2",
|
"@trpc/client": "^11.4.2",
|
||||||
"@trpc/next": "^11.4.2",
|
"@trpc/next": "^11.4.2",
|
||||||
"@trpc/react-query": "^11.4.2",
|
"@trpc/react-query": "^11.4.2",
|
||||||
@@ -92,15 +92,15 @@
|
|||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"@types/chroma-js": "3.1.1",
|
"@types/chroma-js": "3.1.1",
|
||||||
"@types/node": "^22.15.32",
|
"@types/node": "^22.15.33",
|
||||||
"@types/prismjs": "^1.26.5",
|
"@types/prismjs": "^1.26.5",
|
||||||
"@types/react": "19.1.8",
|
"@types/react": "19.1.8",
|
||||||
"@types/react-dom": "19.1.6",
|
"@types/react-dom": "19.1.6",
|
||||||
"@types/swagger-ui-react": "^5.18.0",
|
"@types/swagger-ui-react": "^5.18.0",
|
||||||
"concurrently": "^9.1.2",
|
"concurrently": "^9.2.0",
|
||||||
"eslint": "^9.29.0",
|
"eslint": "^9.29.0",
|
||||||
"node-loader": "^2.1.0",
|
"node-loader": "^2.1.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.6.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
-1
@@ -1,4 +1,4 @@
|
|||||||
import { IconGrid3x3, IconKey, IconPassword, IconServer, IconUser } from "@tabler/icons-react";
|
import { IconGrid3x3, IconKey, IconMessage, IconPassword, IconServer, IconUser } from "@tabler/icons-react";
|
||||||
|
|
||||||
import type { IntegrationSecretKind } from "@homarr/definitions";
|
import type { IntegrationSecretKind } from "@homarr/definitions";
|
||||||
import type { TablerIcon } from "@homarr/ui";
|
import type { TablerIcon } from "@homarr/ui";
|
||||||
@@ -9,4 +9,5 @@ export const integrationSecretIcons = {
|
|||||||
password: IconPassword,
|
password: IconPassword,
|
||||||
realm: IconServer,
|
realm: IconServer,
|
||||||
tokenId: IconGrid3x3,
|
tokenId: IconGrid3x3,
|
||||||
|
topic: IconMessage,
|
||||||
} satisfies Record<IntegrationSecretKind, TablerIcon>;
|
} satisfies Record<IntegrationSecretKind, TablerIcon>;
|
||||||
@@ -44,11 +44,11 @@
|
|||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"@types/node": "^22.15.32",
|
"@types/node": "^22.15.33",
|
||||||
"dotenv-cli": "^8.0.0",
|
"dotenv-cli": "^8.0.0",
|
||||||
"esbuild": "^0.25.5",
|
"esbuild": "^0.25.5",
|
||||||
"eslint": "^9.29.0",
|
"eslint": "^9.29.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.6.0",
|
||||||
"tsx": "4.20.3",
|
"tsx": "4.20.3",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
"@types/ws": "^8.18.1",
|
"@types/ws": "^8.18.1",
|
||||||
"esbuild": "^0.25.5",
|
"esbuild": "^0.25.5",
|
||||||
"eslint": "^9.29.0",
|
"eslint": "^9.29.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.6.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+3
-3
@@ -39,13 +39,13 @@
|
|||||||
"@semantic-release/npm": "^12.0.1",
|
"@semantic-release/npm": "^12.0.1",
|
||||||
"@semantic-release/release-notes-generator": "^14.0.3",
|
"@semantic-release/release-notes-generator": "^14.0.3",
|
||||||
"@turbo/gen": "^2.5.4",
|
"@turbo/gen": "^2.5.4",
|
||||||
"@vitejs/plugin-react": "^4.5.2",
|
"@vitejs/plugin-react": "^4.6.0",
|
||||||
"@vitest/coverage-v8": "^3.2.4",
|
"@vitest/coverage-v8": "^3.2.4",
|
||||||
"@vitest/ui": "^3.2.4",
|
"@vitest/ui": "^3.2.4",
|
||||||
"conventional-changelog-conventionalcommits": "^9.0.0",
|
"conventional-changelog-conventionalcommits": "^9.0.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"jsdom": "^26.1.0",
|
"jsdom": "^26.1.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.6.0",
|
||||||
"semantic-release": "^24.2.5",
|
"semantic-release": "^24.2.5",
|
||||||
"testcontainers": "^11.0.3",
|
"testcontainers": "^11.0.3",
|
||||||
"turbo": "^2.5.4",
|
"turbo": "^2.5.4",
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
"vite-tsconfig-paths": "^5.1.4",
|
"vite-tsconfig-paths": "^5.1.4",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^3.2.4"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.12.1",
|
"packageManager": "pnpm@10.12.3",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=22.16.0"
|
"node": ">=22.16.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
"@homarr/server-settings": "workspace:^0.1.0",
|
"@homarr/server-settings": "workspace:^0.1.0",
|
||||||
"@homarr/validation": "workspace:^0.1.0",
|
"@homarr/validation": "workspace:^0.1.0",
|
||||||
"@kubernetes/client-node": "^1.3.0",
|
"@kubernetes/client-node": "^1.3.0",
|
||||||
"@tanstack/react-query": "^5.80.10",
|
"@tanstack/react-query": "^5.81.2",
|
||||||
"@trpc/client": "^11.4.2",
|
"@trpc/client": "^11.4.2",
|
||||||
"@trpc/react-query": "^11.4.2",
|
"@trpc/react-query": "^11.4.2",
|
||||||
"@trpc/server": "^11.4.2",
|
"@trpc/server": "^11.4.2",
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.29.0",
|
"eslint": "^9.29.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.6.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,7 @@ import { mediaTranscodingRouter } from "./media-transcoding";
|
|||||||
import { minecraftRouter } from "./minecraft";
|
import { minecraftRouter } from "./minecraft";
|
||||||
import { networkControllerRouter } from "./network-controller";
|
import { networkControllerRouter } from "./network-controller";
|
||||||
import { notebookRouter } from "./notebook";
|
import { notebookRouter } from "./notebook";
|
||||||
|
import { notificationsRouter } from "./notifications";
|
||||||
import { optionsRouter } from "./options";
|
import { optionsRouter } from "./options";
|
||||||
import { releasesRouter } from "./releases";
|
import { releasesRouter } from "./releases";
|
||||||
import { rssFeedRouter } from "./rssFeed";
|
import { rssFeedRouter } from "./rssFeed";
|
||||||
@@ -37,4 +38,5 @@ export const widgetRouter = createTRPCRouter({
|
|||||||
options: optionsRouter,
|
options: optionsRouter,
|
||||||
releases: releasesRouter,
|
releases: releasesRouter,
|
||||||
networkController: networkControllerRouter,
|
networkController: networkControllerRouter,
|
||||||
|
notifications: notificationsRouter,
|
||||||
});
|
});
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { observable } from "@trpc/server/observable";
|
||||||
|
|
||||||
|
import type { Modify } from "@homarr/common/types";
|
||||||
|
import type { Integration } from "@homarr/db/schema";
|
||||||
|
import type { IntegrationKindByCategory } from "@homarr/definitions";
|
||||||
|
import { getIntegrationKindsByCategory } from "@homarr/definitions";
|
||||||
|
import type { Notification } from "@homarr/integrations";
|
||||||
|
import { notificationsRequestHandler } from "@homarr/request-handler/notifications";
|
||||||
|
|
||||||
|
import type { IntegrationAction } from "../../middlewares/integration";
|
||||||
|
import { createManyIntegrationMiddleware } from "../../middlewares/integration";
|
||||||
|
import { createTRPCRouter, publicProcedure } from "../../trpc";
|
||||||
|
|
||||||
|
const createNotificationsIntegrationMiddleware = (action: IntegrationAction) =>
|
||||||
|
createManyIntegrationMiddleware(action, ...getIntegrationKindsByCategory("notifications"));
|
||||||
|
|
||||||
|
export const notificationsRouter = createTRPCRouter({
|
||||||
|
getNotifications: publicProcedure
|
||||||
|
.unstable_concat(createNotificationsIntegrationMiddleware("query"))
|
||||||
|
.query(async ({ ctx }) => {
|
||||||
|
return await Promise.all(
|
||||||
|
ctx.integrations.map(async (integration) => {
|
||||||
|
const innerHandler = notificationsRequestHandler.handler(integration, {});
|
||||||
|
const { data, timestamp } = await innerHandler.getCachedOrUpdatedDataAsync({ forceUpdate: false });
|
||||||
|
|
||||||
|
return {
|
||||||
|
integration: {
|
||||||
|
id: integration.id,
|
||||||
|
name: integration.name,
|
||||||
|
kind: integration.kind,
|
||||||
|
updatedAt: timestamp,
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
subscribeNotifications: publicProcedure
|
||||||
|
.unstable_concat(createNotificationsIntegrationMiddleware("query"))
|
||||||
|
.subscription(({ ctx }) => {
|
||||||
|
return observable<{
|
||||||
|
integration: Modify<Integration, { kind: IntegrationKindByCategory<"notifications"> }>;
|
||||||
|
data: Notification[];
|
||||||
|
}>((emit) => {
|
||||||
|
const unsubscribes: (() => void)[] = [];
|
||||||
|
for (const integrationWithSecrets of ctx.integrations) {
|
||||||
|
const { decryptedSecrets: _, ...integration } = integrationWithSecrets;
|
||||||
|
const innerHandler = notificationsRequestHandler.handler(integrationWithSecrets, {});
|
||||||
|
const unsubscribe = innerHandler.subscribe((data) => {
|
||||||
|
emit.next({
|
||||||
|
integration,
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
unsubscribes.push(unsubscribe);
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
unsubscribes.forEach((unsubscribe) => {
|
||||||
|
unsubscribe();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
});
|
||||||
@@ -23,8 +23,8 @@
|
|||||||
},
|
},
|
||||||
"prettier": "@homarr/prettier-config",
|
"prettier": "@homarr/prettier-config",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@auth/core": "^0.39.1",
|
"@auth/core": "^0.40.0",
|
||||||
"@auth/drizzle-adapter": "^1.9.1",
|
"@auth/drizzle-adapter": "^1.10.0",
|
||||||
"@homarr/certificates": "workspace:^0.1.0",
|
"@homarr/certificates": "workspace:^0.1.0",
|
||||||
"@homarr/common": "workspace:^0.1.0",
|
"@homarr/common": "workspace:^0.1.0",
|
||||||
"@homarr/db": "workspace:^0.1.0",
|
"@homarr/db": "workspace:^0.1.0",
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
"cookies": "^0.9.1",
|
"cookies": "^0.9.1",
|
||||||
"ldapts": "8.0.1",
|
"ldapts": "8.0.1",
|
||||||
"next": "15.3.4",
|
"next": "15.3.4",
|
||||||
"next-auth": "5.0.0-beta.28",
|
"next-auth": "5.0.0-beta.29",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
"react-dom": "19.1.0",
|
"react-dom": "19.1.0",
|
||||||
"zod": "^3.25.67"
|
"zod": "^3.25.67"
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
"@types/bcrypt": "5.0.2",
|
"@types/bcrypt": "5.0.2",
|
||||||
"@types/cookies": "0.9.1",
|
"@types/cookies": "0.9.1",
|
||||||
"eslint": "^9.29.0",
|
"eslint": "^9.29.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.6.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,11 +10,11 @@ const calculateTimeAgo = (timestamp: Date) => {
|
|||||||
return dayjs().to(timestamp);
|
return dayjs().to(timestamp);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useTimeAgo = (timestamp: Date) => {
|
export const useTimeAgo = (timestamp: Date, updateFrequency = 1000) => {
|
||||||
const [timeAgo, setTimeAgo] = useState(calculateTimeAgo(timestamp));
|
const [timeAgo, setTimeAgo] = useState(calculateTimeAgo(timestamp));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const intervalId = setInterval(() => setTimeAgo(calculateTimeAgo(timestamp)), 1000); // update every second
|
const intervalId = setInterval(() => setTimeAgo(calculateTimeAgo(timestamp)), updateFrequency);
|
||||||
|
|
||||||
return () => clearInterval(intervalId); // clear interval on hook unmount
|
return () => clearInterval(intervalId); // clear interval on hook unmount
|
||||||
}, [timestamp]);
|
}, [timestamp]);
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export const cronJobs = {
|
|||||||
minecraftServerStatus: { preventManualExecution: false },
|
minecraftServerStatus: { preventManualExecution: false },
|
||||||
networkController: { preventManualExecution: false },
|
networkController: { preventManualExecution: false },
|
||||||
dockerContainers: { preventManualExecution: false },
|
dockerContainers: { preventManualExecution: false },
|
||||||
|
refreshNotifications: { preventManualExecution: false },
|
||||||
} satisfies Record<JobGroupKeys, { preventManualExecution?: boolean }>;
|
} satisfies Record<JobGroupKeys, { preventManualExecution?: boolean }>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { mediaRequestListJob, mediaRequestStatsJob } from "./jobs/integrations/m
|
|||||||
import { mediaServerJob } from "./jobs/integrations/media-server";
|
import { mediaServerJob } from "./jobs/integrations/media-server";
|
||||||
import { mediaTranscodingJob } from "./jobs/integrations/media-transcoding";
|
import { mediaTranscodingJob } from "./jobs/integrations/media-transcoding";
|
||||||
import { networkControllerJob } from "./jobs/integrations/network-controller";
|
import { networkControllerJob } from "./jobs/integrations/network-controller";
|
||||||
|
import { refreshNotificationsJob } from "./jobs/integrations/notifications";
|
||||||
import { minecraftServerStatusJob } from "./jobs/minecraft-server-status";
|
import { minecraftServerStatusJob } from "./jobs/minecraft-server-status";
|
||||||
import { pingJob } from "./jobs/ping";
|
import { pingJob } from "./jobs/ping";
|
||||||
import { rssFeedsJob } from "./jobs/rss-feeds";
|
import { rssFeedsJob } from "./jobs/rss-feeds";
|
||||||
@@ -38,6 +39,7 @@ export const jobGroup = createCronJobGroup({
|
|||||||
minecraftServerStatus: minecraftServerStatusJob,
|
minecraftServerStatus: minecraftServerStatusJob,
|
||||||
dockerContainers: dockerContainersJob,
|
dockerContainers: dockerContainersJob,
|
||||||
networkController: networkControllerJob,
|
networkController: networkControllerJob,
|
||||||
|
refreshNotifications: refreshNotificationsJob,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type JobGroupKeys = ReturnType<(typeof jobGroup)["getKeys"]>[number];
|
export type JobGroupKeys = ReturnType<(typeof jobGroup)["getKeys"]>[number];
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { EVERY_5_MINUTES } from "@homarr/cron-jobs-core/expressions";
|
||||||
|
import { createRequestIntegrationJobHandler } from "@homarr/request-handler/lib/cached-request-integration-job-handler";
|
||||||
|
import { notificationsRequestHandler } from "@homarr/request-handler/notifications";
|
||||||
|
|
||||||
|
import { createCronJob } from "../../lib";
|
||||||
|
|
||||||
|
export const refreshNotificationsJob = createCronJob("refreshNotifications", EVERY_5_MINUTES).withCallback(
|
||||||
|
createRequestIntegrationJobHandler(notificationsRequestHandler.handler, {
|
||||||
|
widgetKinds: ["notifications"],
|
||||||
|
getInput: {
|
||||||
|
notifications: (options) => options,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
// TODO: add dependency
|
||||||
|
import SuperJSON from "superjson";
|
||||||
|
|
||||||
|
import type { IntegrationKind } from "@homarr/definitions";
|
||||||
|
|
||||||
|
import { eq, or } from "../..";
|
||||||
|
import type { Database } from "../..";
|
||||||
|
import { items } from "../../schema";
|
||||||
|
|
||||||
|
export async function migrateReleaseWidgetProviderToOptionsAsync(db: Database) {
|
||||||
|
const existingItems = await db.query.items.findMany({
|
||||||
|
where: (items, { eq }) => eq(items.kind, "releases"),
|
||||||
|
});
|
||||||
|
|
||||||
|
const providerIntegrations = await db.query.integrations.findMany({
|
||||||
|
where: (integrations, { eq }) => or(eq(integrations.kind, "github"), eq(integrations.kind, "gitlab")),
|
||||||
|
columns: {
|
||||||
|
id: true,
|
||||||
|
kind: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const providerIntegrationMap = new Map<IntegrationKind, string>(
|
||||||
|
providerIntegrations.map((integration) => [integration.kind, integration.id]),
|
||||||
|
);
|
||||||
|
|
||||||
|
const updates: {
|
||||||
|
id: string;
|
||||||
|
options: object;
|
||||||
|
}[] = [];
|
||||||
|
for (const item of existingItems) {
|
||||||
|
const options = SuperJSON.parse<object>(item.options);
|
||||||
|
if (!("repositories" in options)) continue;
|
||||||
|
if (!Array.isArray(options.repositories)) continue;
|
||||||
|
if (options.repositories.length === 0) continue;
|
||||||
|
if (!options.repositories.some((repository) => "providerKey" in repository)) continue;
|
||||||
|
|
||||||
|
const updatedRepositories = options.repositories.map(({ providerKey, ...otherFields }) => ({
|
||||||
|
providerIntegrationId: providerIntegrationMap.get(providerKey) ?? null, // Or do custom mapping if needed
|
||||||
|
...otherFields,
|
||||||
|
}));
|
||||||
|
|
||||||
|
updates.push({
|
||||||
|
id: item.id,
|
||||||
|
options: {
|
||||||
|
...options,
|
||||||
|
repositories: updatedRepositories,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const update of updates) {
|
||||||
|
await db
|
||||||
|
.update(items)
|
||||||
|
.set({
|
||||||
|
options: SuperJSON.stringify(update.options),
|
||||||
|
})
|
||||||
|
.where(eq(items.id, update.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Migrated release widget providers to integrations count="${updates.length}"`);
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import type { Database } from "../..";
|
||||||
|
import { migrateReleaseWidgetProviderToOptionsAsync } from "./0000_release_widget_provider_to_options";
|
||||||
|
|
||||||
|
export const applyCustomMigrationsAsync = async (db: Database) => {
|
||||||
|
await migrateReleaseWidgetProviderToOptionsAsync(db);
|
||||||
|
};
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { applyCustomMigrationsAsync } from ".";
|
||||||
|
import { database } from "../../driver";
|
||||||
|
|
||||||
|
applyCustomMigrationsAsync(database)
|
||||||
|
.then(() => {
|
||||||
|
console.log("Custom migrations applied successfully");
|
||||||
|
process.exit(0);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log("Failed to apply custom migrations\n\t", err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
@@ -5,6 +5,7 @@ import mysql from "mysql2";
|
|||||||
import type { Database } from "../..";
|
import type { Database } from "../..";
|
||||||
import { env } from "../../env";
|
import { env } from "../../env";
|
||||||
import * as mysqlSchema from "../../schema/mysql";
|
import * as mysqlSchema from "../../schema/mysql";
|
||||||
|
import { applyCustomMigrationsAsync } from "../custom";
|
||||||
import { seedDataAsync } from "../seed";
|
import { seedDataAsync } from "../seed";
|
||||||
|
|
||||||
const migrationsFolder = process.argv[2] ?? ".";
|
const migrationsFolder = process.argv[2] ?? ".";
|
||||||
@@ -30,6 +31,7 @@ const migrateAsync = async () => {
|
|||||||
|
|
||||||
await migrate(db, { migrationsFolder });
|
await migrate(db, { migrationsFolder });
|
||||||
await seedDataAsync(db as unknown as Database);
|
await seedDataAsync(db as unknown as Database);
|
||||||
|
await applyCustomMigrationsAsync(db as unknown as Database);
|
||||||
};
|
};
|
||||||
|
|
||||||
migrateAsync()
|
migrateAsync()
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
|||||||
|
|
||||||
import { env } from "../../env";
|
import { env } from "../../env";
|
||||||
import * as sqliteSchema from "../../schema/sqlite";
|
import * as sqliteSchema from "../../schema/sqlite";
|
||||||
|
import { applyCustomMigrationsAsync } from "../custom";
|
||||||
import { seedDataAsync } from "../seed";
|
import { seedDataAsync } from "../seed";
|
||||||
|
|
||||||
const migrationsFolder = process.argv[2] ?? ".";
|
const migrationsFolder = process.argv[2] ?? ".";
|
||||||
@@ -16,6 +17,7 @@ const migrateAsync = async () => {
|
|||||||
migrate(db, { migrationsFolder });
|
migrate(db, { migrationsFolder });
|
||||||
|
|
||||||
await seedDataAsync(db);
|
await seedDataAsync(db);
|
||||||
|
await applyCustomMigrationsAsync(db);
|
||||||
};
|
};
|
||||||
|
|
||||||
migrateAsync()
|
migrateAsync()
|
||||||
|
|||||||
@@ -23,12 +23,13 @@
|
|||||||
"clean": "rm -rf .turbo node_modules",
|
"clean": "rm -rf .turbo node_modules",
|
||||||
"format": "prettier --check . --ignore-path ../../.gitignore",
|
"format": "prettier --check . --ignore-path ../../.gitignore",
|
||||||
"lint": "eslint",
|
"lint": "eslint",
|
||||||
|
"migration:custom": "pnpm with-env tsx ./migrations/custom/run-custom.ts",
|
||||||
"migration:mysql:drop": "pnpm with-env drizzle-kit drop --config ./configs/mysql.config.ts",
|
"migration:mysql:drop": "pnpm with-env drizzle-kit drop --config ./configs/mysql.config.ts",
|
||||||
"migration:mysql:generate": "pnpm with-env drizzle-kit generate --config ./configs/mysql.config.ts",
|
"migration:mysql:generate": "pnpm with-env drizzle-kit generate --config ./configs/mysql.config.ts",
|
||||||
"migration:mysql:run": "pnpm with-env drizzle-kit migrate --config ./configs/mysql.config.ts && pnpm run seed",
|
"migration:mysql:run": "pnpm with-env drizzle-kit migrate --config ./configs/mysql.config.ts && pnpm run seed && pnpm run migration:custom",
|
||||||
"migration:sqlite:drop": "pnpm with-env drizzle-kit drop --config ./configs/sqlite.config.ts",
|
"migration:sqlite:drop": "pnpm with-env drizzle-kit drop --config ./configs/sqlite.config.ts",
|
||||||
"migration:sqlite:generate": "pnpm with-env drizzle-kit generate --config ./configs/sqlite.config.ts",
|
"migration:sqlite:generate": "pnpm with-env drizzle-kit generate --config ./configs/sqlite.config.ts",
|
||||||
"migration:sqlite:run": "pnpm with-env drizzle-kit migrate --config ./configs/sqlite.config.ts && pnpm run seed",
|
"migration:sqlite:run": "pnpm with-env drizzle-kit migrate --config ./configs/sqlite.config.ts && pnpm run seed && pnpm run migration:custom",
|
||||||
"push:mysql": "pnpm with-env drizzle-kit push --config ./configs/mysql.config.ts",
|
"push:mysql": "pnpm with-env drizzle-kit push --config ./configs/mysql.config.ts",
|
||||||
"push:sqlite": "pnpm with-env drizzle-kit push --config ./configs/sqlite.config.ts",
|
"push:sqlite": "pnpm with-env drizzle-kit push --config ./configs/sqlite.config.ts",
|
||||||
"seed": "pnpm with-env tsx ./migrations/run-seed.ts",
|
"seed": "pnpm with-env tsx ./migrations/run-seed.ts",
|
||||||
@@ -38,7 +39,7 @@
|
|||||||
},
|
},
|
||||||
"prettier": "@homarr/prettier-config",
|
"prettier": "@homarr/prettier-config",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@auth/core": "^0.39.1",
|
"@auth/core": "^0.40.0",
|
||||||
"@homarr/common": "workspace:^0.1.0",
|
"@homarr/common": "workspace:^0.1.0",
|
||||||
"@homarr/definitions": "workspace:^0.1.0",
|
"@homarr/definitions": "workspace:^0.1.0",
|
||||||
"@homarr/env": "workspace:^0.1.0",
|
"@homarr/env": "workspace:^0.1.0",
|
||||||
@@ -47,9 +48,9 @@
|
|||||||
"@mantine/core": "^8.1.1",
|
"@mantine/core": "^8.1.1",
|
||||||
"@paralleldrive/cuid2": "^2.2.2",
|
"@paralleldrive/cuid2": "^2.2.2",
|
||||||
"@testcontainers/mysql": "^11.0.3",
|
"@testcontainers/mysql": "^11.0.3",
|
||||||
"better-sqlite3": "^11.10.0",
|
"better-sqlite3": "^12.1.0",
|
||||||
"dotenv": "^16.5.0",
|
"dotenv": "^16.5.0",
|
||||||
"drizzle-kit": "^0.31.1",
|
"drizzle-kit": "^0.31.2",
|
||||||
"drizzle-orm": "^0.44.2",
|
"drizzle-orm": "^0.44.2",
|
||||||
"drizzle-zod": "^0.7.1",
|
"drizzle-zod": "^0.7.1",
|
||||||
"mysql2": "3.14.1"
|
"mysql2": "3.14.1"
|
||||||
@@ -62,7 +63,7 @@
|
|||||||
"dotenv-cli": "^8.0.0",
|
"dotenv-cli": "^8.0.0",
|
||||||
"esbuild": "^0.25.5",
|
"esbuild": "^0.25.5",
|
||||||
"eslint": "^9.29.0",
|
"eslint": "^9.29.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.6.0",
|
||||||
"tsx": "4.20.3",
|
"tsx": "4.20.3",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export const integrationSecretKindObject = {
|
|||||||
password: { isPublic: false },
|
password: { isPublic: false },
|
||||||
tokenId: { isPublic: true },
|
tokenId: { isPublic: true },
|
||||||
realm: { isPublic: true },
|
realm: { isPublic: true },
|
||||||
|
topic: { isPublic: true },
|
||||||
} satisfies Record<string, { isPublic: boolean }>;
|
} satisfies Record<string, { isPublic: boolean }>;
|
||||||
|
|
||||||
export const integrationSecretKinds = objectKeys(integrationSecretKindObject);
|
export const integrationSecretKinds = objectKeys(integrationSecretKindObject);
|
||||||
@@ -169,6 +170,12 @@ export const integrationDefs = {
|
|||||||
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/unifi.png",
|
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/unifi.png",
|
||||||
category: ["networkController"],
|
category: ["networkController"],
|
||||||
},
|
},
|
||||||
|
ntfy: {
|
||||||
|
name: "ntfy",
|
||||||
|
secretKinds: [["topic"], ["topic", "apiKey"]],
|
||||||
|
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/ntfy.svg",
|
||||||
|
category: ["notifications"],
|
||||||
|
},
|
||||||
} as const satisfies Record<string, integrationDefinition>;
|
} as const satisfies Record<string, integrationDefinition>;
|
||||||
|
|
||||||
export const integrationKinds = objectKeys(integrationDefs) as AtLeastOneOf<IntegrationKind>;
|
export const integrationKinds = objectKeys(integrationDefs) as AtLeastOneOf<IntegrationKind>;
|
||||||
@@ -223,4 +230,5 @@ export type IntegrationCategory =
|
|||||||
| "healthMonitoring"
|
| "healthMonitoring"
|
||||||
| "search"
|
| "search"
|
||||||
| "mediaTranscoding"
|
| "mediaTranscoding"
|
||||||
| "networkController";
|
| "networkController"
|
||||||
|
| "notifications";
|
||||||
@@ -25,5 +25,6 @@ export const widgetKinds = [
|
|||||||
"healthMonitoring",
|
"healthMonitoring",
|
||||||
"releases",
|
"releases",
|
||||||
"dockerContainers",
|
"dockerContainers",
|
||||||
|
"notifications",
|
||||||
] as const;
|
] as const;
|
||||||
export type WidgetKind = (typeof widgetKinds)[number];
|
export type WidgetKind = (typeof widgetKinds)[number];
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
"@homarr/translation": "workspace:^0.1.0",
|
"@homarr/translation": "workspace:^0.1.0",
|
||||||
"@homarr/validation": "workspace:^0.1.0",
|
"@homarr/validation": "workspace:^0.1.0",
|
||||||
"@jellyfin/sdk": "^0.11.0",
|
"@jellyfin/sdk": "^0.11.0",
|
||||||
"maria2": "^0.4.0",
|
"maria2": "^0.4.1",
|
||||||
"node-ical": "^0.20.1",
|
"node-ical": "^0.20.1",
|
||||||
"proxmox-api": "1.1.1",
|
"proxmox-api": "1.1.1",
|
||||||
"tsdav": "^2.1.5",
|
"tsdav": "^2.1.5",
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import { ReadarrIntegration } from "../media-organizer/readarr/readarr-integrati
|
|||||||
import { SonarrIntegration } from "../media-organizer/sonarr/sonarr-integration";
|
import { SonarrIntegration } from "../media-organizer/sonarr/sonarr-integration";
|
||||||
import { TdarrIntegration } from "../media-transcoding/tdarr-integration";
|
import { TdarrIntegration } from "../media-transcoding/tdarr-integration";
|
||||||
import { NextcloudIntegration } from "../nextcloud/nextcloud.integration";
|
import { NextcloudIntegration } from "../nextcloud/nextcloud.integration";
|
||||||
|
import { NTFYIntegration } from "../ntfy/ntfy-integration";
|
||||||
import { OpenMediaVaultIntegration } from "../openmediavault/openmediavault-integration";
|
import { OpenMediaVaultIntegration } from "../openmediavault/openmediavault-integration";
|
||||||
import { OverseerrIntegration } from "../overseerr/overseerr-integration";
|
import { OverseerrIntegration } from "../overseerr/overseerr-integration";
|
||||||
import { createPiHoleIntegrationAsync } from "../pi-hole/pi-hole-integration-factory";
|
import { createPiHoleIntegrationAsync } from "../pi-hole/pi-hole-integration-factory";
|
||||||
@@ -92,6 +93,7 @@ export const integrationCreators = {
|
|||||||
emby: EmbyIntegration,
|
emby: EmbyIntegration,
|
||||||
nextcloud: NextcloudIntegration,
|
nextcloud: NextcloudIntegration,
|
||||||
unifiController: UnifiControllerIntegration,
|
unifiController: UnifiControllerIntegration,
|
||||||
|
ntfy: NTFYIntegration,
|
||||||
} satisfies Record<IntegrationKind, IntegrationInstance | [(input: IntegrationInput) => Promise<Integration>]>;
|
} satisfies Record<IntegrationKind, IntegrationInstance | [(input: IntegrationInput) => Promise<Integration>]>;
|
||||||
|
|
||||||
type IntegrationInstanceOfKind<TKind extends keyof typeof integrationCreators> = {
|
type IntegrationInstanceOfKind<TKind extends keyof typeof integrationCreators> = {
|
||||||
|
|||||||
@@ -1,26 +1,27 @@
|
|||||||
// General integrations
|
// General integrations
|
||||||
export { AdGuardHomeIntegration } from "./adguard-home/adguard-home-integration";
|
export { AdGuardHomeIntegration } from "./adguard-home/adguard-home-integration";
|
||||||
|
export { Aria2Integration } from "./download-client/aria2/aria2-integration";
|
||||||
export { DelugeIntegration } from "./download-client/deluge/deluge-integration";
|
export { DelugeIntegration } from "./download-client/deluge/deluge-integration";
|
||||||
export { NzbGetIntegration } from "./download-client/nzbget/nzbget-integration";
|
export { NzbGetIntegration } from "./download-client/nzbget/nzbget-integration";
|
||||||
export { QBitTorrentIntegration } from "./download-client/qbittorrent/qbittorrent-integration";
|
export { QBitTorrentIntegration } from "./download-client/qbittorrent/qbittorrent-integration";
|
||||||
export { SabnzbdIntegration } from "./download-client/sabnzbd/sabnzbd-integration";
|
export { SabnzbdIntegration } from "./download-client/sabnzbd/sabnzbd-integration";
|
||||||
export { TransmissionIntegration } from "./download-client/transmission/transmission-integration";
|
export { TransmissionIntegration } from "./download-client/transmission/transmission-integration";
|
||||||
export { Aria2Integration } from "./download-client/aria2/aria2-integration";
|
|
||||||
export { HomeAssistantIntegration } from "./homeassistant/homeassistant-integration";
|
export { HomeAssistantIntegration } from "./homeassistant/homeassistant-integration";
|
||||||
export { DownloadClientIntegration } from "./interfaces/downloads/download-client-integration";
|
export { DownloadClientIntegration } from "./interfaces/downloads/download-client-integration";
|
||||||
export { JellyfinIntegration } from "./jellyfin/jellyfin-integration";
|
export { JellyfinIntegration } from "./jellyfin/jellyfin-integration";
|
||||||
export { JellyseerrIntegration } from "./jellyseerr/jellyseerr-integration";
|
export { JellyseerrIntegration } from "./jellyseerr/jellyseerr-integration";
|
||||||
|
export { LidarrIntegration } from "./media-organizer/lidarr/lidarr-integration";
|
||||||
export { RadarrIntegration } from "./media-organizer/radarr/radarr-integration";
|
export { RadarrIntegration } from "./media-organizer/radarr/radarr-integration";
|
||||||
|
export { ReadarrIntegration } from "./media-organizer/readarr/readarr-integration";
|
||||||
export { SonarrIntegration } from "./media-organizer/sonarr/sonarr-integration";
|
export { SonarrIntegration } from "./media-organizer/sonarr/sonarr-integration";
|
||||||
|
export { NextcloudIntegration } from "./nextcloud/nextcloud.integration";
|
||||||
|
export { NTFYIntegration } from "./ntfy/ntfy-integration";
|
||||||
export { OpenMediaVaultIntegration } from "./openmediavault/openmediavault-integration";
|
export { OpenMediaVaultIntegration } from "./openmediavault/openmediavault-integration";
|
||||||
export { OverseerrIntegration } from "./overseerr/overseerr-integration";
|
export { OverseerrIntegration } from "./overseerr/overseerr-integration";
|
||||||
export { PiHoleIntegrationV5 } from "./pi-hole/v5/pi-hole-integration-v5";
|
export { PiHoleIntegrationV5 } from "./pi-hole/v5/pi-hole-integration-v5";
|
||||||
export { PiHoleIntegrationV6 } from "./pi-hole/v6/pi-hole-integration-v6";
|
export { PiHoleIntegrationV6 } from "./pi-hole/v6/pi-hole-integration-v6";
|
||||||
export { PlexIntegration } from "./plex/plex-integration";
|
export { PlexIntegration } from "./plex/plex-integration";
|
||||||
export { ProwlarrIntegration } from "./prowlarr/prowlarr-integration";
|
export { ProwlarrIntegration } from "./prowlarr/prowlarr-integration";
|
||||||
export { LidarrIntegration } from "./media-organizer/lidarr/lidarr-integration";
|
|
||||||
export { ReadarrIntegration } from "./media-organizer/readarr/readarr-integration";
|
|
||||||
export { NextcloudIntegration } from "./nextcloud/nextcloud.integration";
|
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
export type { IntegrationInput } from "./base/integration";
|
export type { IntegrationInput } from "./base/integration";
|
||||||
@@ -34,6 +35,7 @@ export type { StreamSession } from "./interfaces/media-server/session";
|
|||||||
export type { TdarrQueue } from "./interfaces/media-transcoding/queue";
|
export type { TdarrQueue } from "./interfaces/media-transcoding/queue";
|
||||||
export type { TdarrPieSegment, TdarrStatistics } from "./interfaces/media-transcoding/statistics";
|
export type { TdarrPieSegment, TdarrStatistics } from "./interfaces/media-transcoding/statistics";
|
||||||
export type { TdarrWorker } from "./interfaces/media-transcoding/workers";
|
export type { TdarrWorker } from "./interfaces/media-transcoding/workers";
|
||||||
|
export type { Notification } from "./interfaces/notifications/notification";
|
||||||
|
|
||||||
// Schemas
|
// Schemas
|
||||||
export { downloadClientItemSchema } from "./interfaces/downloads/download-client-items";
|
export { downloadClientItemSchema } from "./interfaces/downloads/download-client-items";
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export interface MediaRequestStats {
|
|||||||
users: RequestUser[];
|
users: RequestUser[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://github.com/fallenbagel/jellyseerr/blob/0fd03f38480f853e7015ad9229ed98160e37602e/server/constants/media.ts#L1
|
||||||
export enum MediaRequestStatus {
|
export enum MediaRequestStatus {
|
||||||
PendingApproval = 1,
|
PendingApproval = 1,
|
||||||
Approved = 2,
|
Approved = 2,
|
||||||
@@ -51,6 +52,7 @@ export enum MediaRequestStatus {
|
|||||||
Completed = 5,
|
Completed = 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://github.com/fallenbagel/jellyseerr/blob/0fd03f38480f853e7015ad9229ed98160e37602e/server/constants/media.ts#L14
|
||||||
export enum MediaAvailability {
|
export enum MediaAvailability {
|
||||||
Unknown = 1,
|
Unknown = 1,
|
||||||
Pending = 2,
|
Pending = 2,
|
||||||
@@ -58,4 +60,5 @@ export enum MediaAvailability {
|
|||||||
PartiallyAvailable = 4,
|
PartiallyAvailable = 4,
|
||||||
Available = 5,
|
Available = 5,
|
||||||
Blacklisted = 6,
|
Blacklisted = 6,
|
||||||
|
Deleted = 7,
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export interface Notification {
|
||||||
|
id: string;
|
||||||
|
time: Date;
|
||||||
|
title: string;
|
||||||
|
body: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { Integration } from "../../base/integration";
|
||||||
|
import type { Notification } from "./notification";
|
||||||
|
|
||||||
|
export abstract class NotificationsIntegration extends Integration {
|
||||||
|
public abstract getNotificationsAsync(): Promise<Notification[]>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import { fetchWithTrustedCertificatesAsync } from "@homarr/certificates/server";
|
||||||
|
import { ResponseError } from "@homarr/common/server";
|
||||||
|
|
||||||
|
import type { IntegrationTestingInput } from "../base/integration";
|
||||||
|
import type { TestingResult } from "../base/test-connection/test-connection-service";
|
||||||
|
import type { Notification } from "../interfaces/notifications/notification";
|
||||||
|
import { NotificationsIntegration } from "../interfaces/notifications/notifications-integration";
|
||||||
|
import { ntfyNotificationSchema } from "./ntfy-schema";
|
||||||
|
|
||||||
|
export class NTFYIntegration extends NotificationsIntegration {
|
||||||
|
public async testingAsync(input: IntegrationTestingInput): Promise<TestingResult> {
|
||||||
|
await input.fetchAsync(this.url("/v1/account"), { headers: this.getHeaders() });
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
private getTopicURL() {
|
||||||
|
return this.url(`/${encodeURIComponent(super.getSecretValue("topic"))}/json`, { poll: 1 });
|
||||||
|
}
|
||||||
|
private getHeaders() {
|
||||||
|
return this.hasSecretValue("apiKey") ? { Authorization: `Bearer ${super.getSecretValue("apiKey")}` } : {};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getNotificationsAsync() {
|
||||||
|
const url = this.getTopicURL();
|
||||||
|
const notifications = await Promise.all(
|
||||||
|
(
|
||||||
|
await fetchWithTrustedCertificatesAsync(url, { headers: this.getHeaders() })
|
||||||
|
.then((response) => {
|
||||||
|
if (!response.ok) throw new ResponseError(response);
|
||||||
|
return response.text();
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
if (error instanceof Error) throw error;
|
||||||
|
else {
|
||||||
|
throw new Error("Error communicating with ntfy");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
// response is provided as individual lines of JSON
|
||||||
|
.split("\n")
|
||||||
|
.map(async (line) => {
|
||||||
|
// ignore empty lines
|
||||||
|
if (line.length === 0) return null;
|
||||||
|
|
||||||
|
const json = JSON.parse(line) as unknown;
|
||||||
|
const parsed = await ntfyNotificationSchema.parseAsync(json);
|
||||||
|
if (parsed.event === "message") return parsed;
|
||||||
|
// ignore non-event messages
|
||||||
|
else return null;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return notifications
|
||||||
|
.filter((notification) => notification !== null)
|
||||||
|
.map((notification): Notification => {
|
||||||
|
const topicURL = this.url(`/${notification.topic}`);
|
||||||
|
return {
|
||||||
|
id: notification.id,
|
||||||
|
time: new Date(notification.time * 1000),
|
||||||
|
title: notification.title ?? topicURL.hostname + topicURL.pathname,
|
||||||
|
body: notification.message,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
// There are more properties, see: https://docs.ntfy.sh/subscribe/api/#json-message-format
|
||||||
|
// Not all properties are required for this use case.
|
||||||
|
export const ntfyNotificationSchema = z.object({
|
||||||
|
id: z.string(),
|
||||||
|
time: z.number(),
|
||||||
|
event: z.string(), // we only care about "message"
|
||||||
|
topic: z.string(),
|
||||||
|
title: z.optional(z.string()),
|
||||||
|
message: z.string(),
|
||||||
|
});
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
import type { IntegrationKindByCategory } from "@homarr/definitions";
|
||||||
|
import type { Notification } from "@homarr/integrations";
|
||||||
|
import { createIntegrationAsync } from "@homarr/integrations";
|
||||||
|
|
||||||
|
import { createCachedIntegrationRequestHandler } from "./lib/cached-integration-request-handler";
|
||||||
|
|
||||||
|
export const notificationsRequestHandler = createCachedIntegrationRequestHandler<
|
||||||
|
Notification[],
|
||||||
|
IntegrationKindByCategory<"notifications">,
|
||||||
|
Record<string, never>
|
||||||
|
>({
|
||||||
|
async requestAsync(integration) {
|
||||||
|
const integrationInstance = await createIntegrationAsync(integration);
|
||||||
|
return await integrationInstance.getNotificationsAsync();
|
||||||
|
},
|
||||||
|
cacheDuration: dayjs.duration(5, "minutes"),
|
||||||
|
queryKey: "notificationsJobStatus",
|
||||||
|
});
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
"deepmerge": "4.3.1",
|
"deepmerge": "4.3.1",
|
||||||
"mantine-react-table": "2.0.0-beta.9",
|
"mantine-react-table": "2.0.0-beta.9",
|
||||||
"next": "15.3.4",
|
"next": "15.3.4",
|
||||||
"next-intl": "4.1.0",
|
"next-intl": "4.3.1",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
"react-dom": "19.1.0"
|
"react-dom": "19.1.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"newLabel": ""
|
"newLabel": ""
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "",
|
"pending": "",
|
||||||
"processing": "",
|
"processing": "",
|
||||||
"partiallyAvailable": "",
|
"partiallyAvailable": "",
|
||||||
"available": ""
|
"available": "",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "",
|
"pending": "",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": ""
|
"internalServerError": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "领域",
|
"label": "领域",
|
||||||
"newLabel": "新领域"
|
"newLabel": "新领域"
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "等待处理",
|
"pending": "等待处理",
|
||||||
"processing": "处理中",
|
"processing": "处理中",
|
||||||
"partiallyAvailable": "部分",
|
"partiallyAvailable": "部分",
|
||||||
"available": "可用"
|
"available": "可用",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "待处理",
|
"pending": "待处理",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": "获取网络控制器概述失败"
|
"internalServerError": "获取网络控制器概述失败"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": "网络控制器"
|
"label": "网络控制器"
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": "Docker 容器"
|
"label": "Docker 容器"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"newLabel": ""
|
"newLabel": ""
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "",
|
"pending": "",
|
||||||
"processing": "",
|
"processing": "",
|
||||||
"partiallyAvailable": "Částečně dostupné",
|
"partiallyAvailable": "Částečně dostupné",
|
||||||
"available": "K dispozici"
|
"available": "K dispozici",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "",
|
"pending": "",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": ""
|
"internalServerError": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "Realm",
|
"label": "Realm",
|
||||||
"newLabel": "Nyt realm"
|
"newLabel": "Nyt realm"
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "Afventende",
|
"pending": "Afventende",
|
||||||
"processing": "Behandler",
|
"processing": "Behandler",
|
||||||
"partiallyAvailable": "Delvis",
|
"partiallyAvailable": "Delvis",
|
||||||
"available": "Tilgængelig"
|
"available": "Tilgængelig",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "Afventende",
|
"pending": "Afventende",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": "Kunne ikke hente Netværkskontroloversigt"
|
"internalServerError": "Kunne ikke hente Netværkskontroloversigt"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": "Netværkskontroller"
|
"label": "Netværkskontroller"
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": "Docker containers"
|
"label": "Docker containers"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "Bereich",
|
"label": "Bereich",
|
||||||
"newLabel": "Neuer Bereich"
|
"newLabel": "Neuer Bereich"
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "Ausstehend",
|
"pending": "Ausstehend",
|
||||||
"processing": "In Bearbeitung",
|
"processing": "In Bearbeitung",
|
||||||
"partiallyAvailable": "Teilweise",
|
"partiallyAvailable": "Teilweise",
|
||||||
"available": "Verfügbar"
|
"available": "Verfügbar",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "Ausstehend",
|
"pending": "Ausstehend",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": ""
|
"internalServerError": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "Bereich",
|
"label": "Bereich",
|
||||||
"newLabel": "Neuer Bereich"
|
"newLabel": "Neuer Bereich"
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "Ausstehend",
|
"pending": "Ausstehend",
|
||||||
"processing": "In Bearbeitung",
|
"processing": "In Bearbeitung",
|
||||||
"partiallyAvailable": "Teilweise",
|
"partiallyAvailable": "Teilweise",
|
||||||
"available": "Verfügbar"
|
"available": "Verfügbar",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "Ausstehend",
|
"pending": "Ausstehend",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": "Fehler beim Abrufen der Netzwerk Controller Zusammenfassung"
|
"internalServerError": "Fehler beim Abrufen der Netzwerk Controller Zusammenfassung"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": "Netzwerk Controller"
|
"label": "Netzwerk Controller"
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"newLabel": ""
|
"newLabel": ""
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "",
|
"pending": "",
|
||||||
"processing": "",
|
"processing": "",
|
||||||
"partiallyAvailable": "Μερικώς",
|
"partiallyAvailable": "Μερικώς",
|
||||||
"available": "Διαθέσιμο"
|
"available": "Διαθέσιμο",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "",
|
"pending": "",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": ""
|
"internalServerError": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "Realm",
|
"label": "Realm",
|
||||||
"newLabel": "New realm"
|
"newLabel": "New realm"
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "",
|
"pending": "",
|
||||||
"processing": "",
|
"processing": "",
|
||||||
"partiallyAvailable": "",
|
"partiallyAvailable": "",
|
||||||
"available": ""
|
"available": "",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "",
|
"pending": "",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": ""
|
"internalServerError": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "Realm",
|
"label": "Realm",
|
||||||
"newLabel": "New realm"
|
"newLabel": "New realm"
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "Topic",
|
||||||
|
"newLabel": "New topic"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "Pending",
|
"pending": "Pending",
|
||||||
"processing": "Processing",
|
"processing": "Processing",
|
||||||
"partiallyAvailable": "Partial",
|
"partiallyAvailable": "Partial",
|
||||||
"available": "Available"
|
"available": "Available",
|
||||||
|
"blacklisted": "Blacklisted",
|
||||||
|
"deleted": "Deleted"
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "Pending",
|
"pending": "Pending",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": "Failed to fetch Network Controller Summary"
|
"internalServerError": "Failed to fetch Network Controller Summary"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "Notifications",
|
||||||
|
"description": "Display notification history from an integration",
|
||||||
|
"noItems": "No notifications to display.",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": "Network Controller"
|
"label": "Network Controller"
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": "Notification Updater"
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": "Docker containers"
|
"label": "Docker containers"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"newLabel": ""
|
"newLabel": ""
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "",
|
"pending": "",
|
||||||
"processing": "",
|
"processing": "",
|
||||||
"partiallyAvailable": "Parcial",
|
"partiallyAvailable": "Parcial",
|
||||||
"available": "Disponible"
|
"available": "Disponible",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "",
|
"pending": "",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": ""
|
"internalServerError": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"newLabel": ""
|
"newLabel": ""
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "",
|
"pending": "",
|
||||||
"processing": "",
|
"processing": "",
|
||||||
"partiallyAvailable": "",
|
"partiallyAvailable": "",
|
||||||
"available": ""
|
"available": "",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "",
|
"pending": "",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": ""
|
"internalServerError": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "Domaine",
|
"label": "Domaine",
|
||||||
"newLabel": "Nouveau domaine"
|
"newLabel": "Nouveau domaine"
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "En attente",
|
"pending": "En attente",
|
||||||
"processing": "Traitement en cours",
|
"processing": "Traitement en cours",
|
||||||
"partiallyAvailable": "Partiel",
|
"partiallyAvailable": "Partiel",
|
||||||
"available": "Disponible"
|
"available": "Disponible",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "En attente",
|
"pending": "En attente",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": ""
|
"internalServerError": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "תחום",
|
"label": "תחום",
|
||||||
"newLabel": "תחום חדש"
|
"newLabel": "תחום חדש"
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "בהמתנה",
|
"pending": "בהמתנה",
|
||||||
"processing": "מעבד",
|
"processing": "מעבד",
|
||||||
"partiallyAvailable": "חלקי",
|
"partiallyAvailable": "חלקי",
|
||||||
"available": "זמין"
|
"available": "זמין",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "ממתין",
|
"pending": "ממתין",
|
||||||
@@ -2227,13 +2233,13 @@
|
|||||||
"label": "הוסף מאגר"
|
"label": "הוסף מאגר"
|
||||||
},
|
},
|
||||||
"importRepositories": {
|
"importRepositories": {
|
||||||
"label": "",
|
"label": "ייבוא מדוקר",
|
||||||
"loading": "",
|
"loading": "טוען תמונות דוקר",
|
||||||
"noImagesFound": "",
|
"noImagesFound": "לא נמצאו תמונות דוקר",
|
||||||
"listFoundImages": "",
|
"listFoundImages": "רשימת התמונות שנמצאו",
|
||||||
"listAlreadyImportedImages": "",
|
"listAlreadyImportedImages": "רשימת התמונות שכבר יובאו",
|
||||||
"allImagesAlreadyImported": "",
|
"allImagesAlreadyImported": "כל התמונות כבר יובאו",
|
||||||
"onlyAdminCanImport": ""
|
"onlyAdminCanImport": "רק מנהלים יכולים לייבא מדוקר"
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
"label": "ספק"
|
"label": "ספק"
|
||||||
@@ -2276,7 +2282,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"importForm": {
|
"importForm": {
|
||||||
"title": ""
|
"title": "ייבוא מדוקר"
|
||||||
},
|
},
|
||||||
"example": {
|
"example": {
|
||||||
"label": "דוגמא"
|
"label": "דוגמא"
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": "אחזור תקציר בקר הרשת נכשל"
|
"internalServerError": "אחזור תקציר בקר הרשת נכשל"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": "בקר רשת"
|
"label": "בקר רשת"
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": "מכולות דוקר"
|
"label": "מכולות דוקר"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"newLabel": ""
|
"newLabel": ""
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "",
|
"pending": "",
|
||||||
"processing": "",
|
"processing": "",
|
||||||
"partiallyAvailable": "",
|
"partiallyAvailable": "",
|
||||||
"available": ""
|
"available": "",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "",
|
"pending": "",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": ""
|
"internalServerError": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"newLabel": ""
|
"newLabel": ""
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "",
|
"pending": "",
|
||||||
"processing": "Feldolgozás",
|
"processing": "Feldolgozás",
|
||||||
"partiallyAvailable": "Részleges",
|
"partiallyAvailable": "Részleges",
|
||||||
"available": "Elérhető"
|
"available": "Elérhető",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "",
|
"pending": "",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": ""
|
"internalServerError": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"newLabel": ""
|
"newLabel": ""
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "",
|
"pending": "",
|
||||||
"processing": "",
|
"processing": "",
|
||||||
"partiallyAvailable": "Parziale",
|
"partiallyAvailable": "Parziale",
|
||||||
"available": "Disponibile"
|
"available": "Disponibile",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "",
|
"pending": "",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": ""
|
"internalServerError": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"newLabel": ""
|
"newLabel": ""
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "",
|
"pending": "",
|
||||||
"processing": "",
|
"processing": "",
|
||||||
"partiallyAvailable": "一部",
|
"partiallyAvailable": "一部",
|
||||||
"available": "利用可能"
|
"available": "利用可能",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "",
|
"pending": "",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": ""
|
"internalServerError": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"newLabel": ""
|
"newLabel": ""
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "",
|
"pending": "",
|
||||||
"processing": "",
|
"processing": "",
|
||||||
"partiallyAvailable": "",
|
"partiallyAvailable": "",
|
||||||
"available": ""
|
"available": "",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "",
|
"pending": "",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": ""
|
"internalServerError": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"newLabel": ""
|
"newLabel": ""
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "",
|
"pending": "",
|
||||||
"processing": "",
|
"processing": "",
|
||||||
"partiallyAvailable": "Dalis",
|
"partiallyAvailable": "Dalis",
|
||||||
"available": "Galima"
|
"available": "Galima",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "",
|
"pending": "",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": ""
|
"internalServerError": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"newLabel": ""
|
"newLabel": ""
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "",
|
"pending": "",
|
||||||
"processing": "",
|
"processing": "",
|
||||||
"partiallyAvailable": "Daļējs",
|
"partiallyAvailable": "Daļējs",
|
||||||
"available": "Pieejams"
|
"available": "Pieejams",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "",
|
"pending": "",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": ""
|
"internalServerError": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "Realm",
|
"label": "Realm",
|
||||||
"newLabel": "Nieuwe Realm"
|
"newLabel": "Nieuwe Realm"
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "In afwachting",
|
"pending": "In afwachting",
|
||||||
"processing": "Verwerken",
|
"processing": "Verwerken",
|
||||||
"partiallyAvailable": "Gedeeltelijk",
|
"partiallyAvailable": "Gedeeltelijk",
|
||||||
"available": "Beschikbaar"
|
"available": "Beschikbaar",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "In afwachting",
|
"pending": "In afwachting",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": "Netwerkcontroller samenvatting ophalen mislukt"
|
"internalServerError": "Netwerkcontroller samenvatting ophalen mislukt"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": "Netwerkcontroller"
|
"label": "Netwerkcontroller"
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "Område",
|
"label": "Område",
|
||||||
"newLabel": "Nytt område"
|
"newLabel": "Nytt område"
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "Pågår",
|
"pending": "Pågår",
|
||||||
"processing": "Prosesserer",
|
"processing": "Prosesserer",
|
||||||
"partiallyAvailable": "Delvis",
|
"partiallyAvailable": "Delvis",
|
||||||
"available": "Tilgjengelig"
|
"available": "Tilgjengelig",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "Venter",
|
"pending": "Venter",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": ""
|
"internalServerError": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"newLabel": ""
|
"newLabel": ""
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "Oczekujący",
|
"pending": "Oczekujący",
|
||||||
"processing": "Przetwarzanie",
|
"processing": "Przetwarzanie",
|
||||||
"partiallyAvailable": "",
|
"partiallyAvailable": "",
|
||||||
"available": "Dostępne"
|
"available": "Dostępne",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "",
|
"pending": "",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": ""
|
"internalServerError": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"newLabel": ""
|
"newLabel": ""
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "",
|
"pending": "",
|
||||||
"processing": "",
|
"processing": "",
|
||||||
"partiallyAvailable": "Parcial",
|
"partiallyAvailable": "Parcial",
|
||||||
"available": "Disponível"
|
"available": "Disponível",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "",
|
"pending": "",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": ""
|
"internalServerError": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"newLabel": ""
|
"newLabel": ""
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "",
|
"pending": "",
|
||||||
"processing": "",
|
"processing": "",
|
||||||
"partiallyAvailable": "Parțial",
|
"partiallyAvailable": "Parțial",
|
||||||
"available": "Disponibil"
|
"available": "Disponibil",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "",
|
"pending": "",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": ""
|
"internalServerError": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "Область",
|
"label": "Область",
|
||||||
"newLabel": "Новая область"
|
"newLabel": "Новая область"
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "В ожидании",
|
"pending": "В ожидании",
|
||||||
"processing": "Обработка",
|
"processing": "Обработка",
|
||||||
"partiallyAvailable": "Частично доступно",
|
"partiallyAvailable": "Частично доступно",
|
||||||
"available": "Доступно"
|
"available": "Доступно",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "В ожидании",
|
"pending": "В ожидании",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": ""
|
"internalServerError": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "Ríša",
|
"label": "Ríša",
|
||||||
"newLabel": "Nová ríša"
|
"newLabel": "Nová ríša"
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "Čakajúce",
|
"pending": "Čakajúce",
|
||||||
"processing": "Spracovanie",
|
"processing": "Spracovanie",
|
||||||
"partiallyAvailable": "Čiastočný",
|
"partiallyAvailable": "Čiastočný",
|
||||||
"available": "K dispozícii"
|
"available": "K dispozícii",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "Čakajúce",
|
"pending": "Čakajúce",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": "Nepodarilo sa načítať súhrn sieťového ovládača"
|
"internalServerError": "Nepodarilo sa načítať súhrn sieťového ovládača"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": "Sieťový ovládač"
|
"label": "Sieťový ovládač"
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": "Docker kontajnery"
|
"label": "Docker kontajnery"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"newLabel": ""
|
"newLabel": ""
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "",
|
"pending": "",
|
||||||
"processing": "",
|
"processing": "",
|
||||||
"partiallyAvailable": "",
|
"partiallyAvailable": "",
|
||||||
"available": ""
|
"available": "",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "",
|
"pending": "",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": ""
|
"internalServerError": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"newLabel": ""
|
"newLabel": ""
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "",
|
"pending": "",
|
||||||
"processing": "",
|
"processing": "",
|
||||||
"partiallyAvailable": "Delmängd",
|
"partiallyAvailable": "Delmängd",
|
||||||
"available": "Tillgänglig"
|
"available": "Tillgänglig",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "",
|
"pending": "",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": ""
|
"internalServerError": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "Erişim Alanı",
|
"label": "Erişim Alanı",
|
||||||
"newLabel": "Yeni erişim alanı"
|
"newLabel": "Yeni erişim alanı"
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "Bekleyen",
|
"pending": "Bekleyen",
|
||||||
"processing": "İşlemde",
|
"processing": "İşlemde",
|
||||||
"partiallyAvailable": "Kısmi",
|
"partiallyAvailable": "Kısmi",
|
||||||
"available": "Mevcut"
|
"available": "Mevcut",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "Bekleyen",
|
"pending": "Bekleyen",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": "Ağ Denetleyicisi Özeti alınamadı"
|
"internalServerError": "Ağ Denetleyicisi Özeti alınamadı"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": "Ağ Denetleyicisi"
|
"label": "Ağ Denetleyicisi"
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": "Docker konteynerleri"
|
"label": "Docker konteynerleri"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"newLabel": ""
|
"newLabel": ""
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "В очікуванні",
|
"pending": "В очікуванні",
|
||||||
"processing": "В обробці",
|
"processing": "В обробці",
|
||||||
"partiallyAvailable": "Частково доступно",
|
"partiallyAvailable": "Частково доступно",
|
||||||
"available": "Доступно"
|
"available": "Доступно",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "",
|
"pending": "",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": ""
|
"internalServerError": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"newLabel": ""
|
"newLabel": ""
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "",
|
"pending": "",
|
||||||
"processing": "",
|
"processing": "",
|
||||||
"partiallyAvailable": "Một phần",
|
"partiallyAvailable": "Một phần",
|
||||||
"available": "Khả dụng"
|
"available": "Khả dụng",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "",
|
"pending": "",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": ""
|
"internalServerError": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
"realm": {
|
"realm": {
|
||||||
"label": "領域",
|
"label": "領域",
|
||||||
"newLabel": "新領域"
|
"newLabel": "新領域"
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"label": "",
|
||||||
|
"newLabel": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2088,7 +2092,9 @@
|
|||||||
"pending": "等待處理中",
|
"pending": "等待處理中",
|
||||||
"processing": "處理中",
|
"processing": "處理中",
|
||||||
"partiallyAvailable": "部分",
|
"partiallyAvailable": "部分",
|
||||||
"available": "待定"
|
"available": "待定",
|
||||||
|
"blacklisted": "",
|
||||||
|
"deleted": ""
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"pending": "待處理",
|
"pending": "待處理",
|
||||||
@@ -2357,6 +2363,12 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"internalServerError": "無法獲取網路控制總覽"
|
"internalServerError": "無法獲取網路控制總覽"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"name": "",
|
||||||
|
"description": "",
|
||||||
|
"noItems": "",
|
||||||
|
"option": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgetPreview": {
|
"widgetPreview": {
|
||||||
@@ -3122,6 +3134,9 @@
|
|||||||
"networkController": {
|
"networkController": {
|
||||||
"label": "網路控制"
|
"label": "網路控制"
|
||||||
},
|
},
|
||||||
|
"refreshNotifications": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"label": ""
|
"label": ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,21 +52,21 @@
|
|||||||
"@mantine/core": "^8.1.1",
|
"@mantine/core": "^8.1.1",
|
||||||
"@mantine/hooks": "^8.1.1",
|
"@mantine/hooks": "^8.1.1",
|
||||||
"@tabler/icons-react": "^3.34.0",
|
"@tabler/icons-react": "^3.34.0",
|
||||||
"@tiptap/extension-color": "2.22.0",
|
"@tiptap/extension-color": "2.22.3",
|
||||||
"@tiptap/extension-highlight": "2.22.0",
|
"@tiptap/extension-highlight": "2.22.3",
|
||||||
"@tiptap/extension-image": "2.22.0",
|
"@tiptap/extension-image": "2.22.3",
|
||||||
"@tiptap/extension-link": "^2.22.0",
|
"@tiptap/extension-link": "^2.22.3",
|
||||||
"@tiptap/extension-table": "2.22.0",
|
"@tiptap/extension-table": "2.22.3",
|
||||||
"@tiptap/extension-table-cell": "2.22.0",
|
"@tiptap/extension-table-cell": "2.22.3",
|
||||||
"@tiptap/extension-table-header": "2.22.0",
|
"@tiptap/extension-table-header": "2.22.3",
|
||||||
"@tiptap/extension-table-row": "2.22.0",
|
"@tiptap/extension-table-row": "2.22.3",
|
||||||
"@tiptap/extension-task-item": "2.22.0",
|
"@tiptap/extension-task-item": "2.22.3",
|
||||||
"@tiptap/extension-task-list": "2.22.0",
|
"@tiptap/extension-task-list": "2.22.3",
|
||||||
"@tiptap/extension-text-align": "2.22.0",
|
"@tiptap/extension-text-align": "2.22.3",
|
||||||
"@tiptap/extension-text-style": "2.22.0",
|
"@tiptap/extension-text-style": "2.22.3",
|
||||||
"@tiptap/extension-underline": "2.22.0",
|
"@tiptap/extension-underline": "2.22.3",
|
||||||
"@tiptap/react": "^2.22.0",
|
"@tiptap/react": "^2.22.3",
|
||||||
"@tiptap/starter-kit": "^2.22.0",
|
"@tiptap/starter-kit": "^2.22.3",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"mantine-react-table": "2.0.0-beta.9",
|
"mantine-react-table": "2.0.0-beta.9",
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
"react-dom": "19.1.0",
|
"react-dom": "19.1.0",
|
||||||
"react-markdown": "^10.1.0",
|
"react-markdown": "^10.1.0",
|
||||||
"recharts": "^2.15.3",
|
"recharts": "^2.15.4",
|
||||||
"video.js": "^8.23.3",
|
"video.js": "^8.23.3",
|
||||||
"zod": "^3.25.67"
|
"zod": "^3.25.67"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import * as minecraftServerStatus from "./minecraft/server-status";
|
|||||||
import * as networkControllerStatus from "./network-controller/network-status";
|
import * as networkControllerStatus from "./network-controller/network-status";
|
||||||
import * as networkControllerSummary from "./network-controller/summary";
|
import * as networkControllerSummary from "./network-controller/summary";
|
||||||
import * as notebook from "./notebook";
|
import * as notebook from "./notebook";
|
||||||
|
import * as notifications from "./notifications";
|
||||||
import type { WidgetOptionDefinition } from "./options";
|
import type { WidgetOptionDefinition } from "./options";
|
||||||
import * as releases from "./releases";
|
import * as releases from "./releases";
|
||||||
import * as rssFeed from "./rssFeed";
|
import * as rssFeed from "./rssFeed";
|
||||||
@@ -67,6 +68,7 @@ export const widgetImports = {
|
|||||||
minecraftServerStatus,
|
minecraftServerStatus,
|
||||||
dockerContainers,
|
dockerContainers,
|
||||||
releases,
|
releases,
|
||||||
|
notifications,
|
||||||
} satisfies WidgetImportRecord;
|
} satisfies WidgetImportRecord;
|
||||||
|
|
||||||
export type WidgetImports = typeof widgetImports;
|
export type WidgetImports = typeof widgetImports;
|
||||||
|
|||||||
@@ -252,7 +252,11 @@ function getAvailabilityProperties(
|
|||||||
return { color: "blue", label: t("availability.processing") };
|
return { color: "blue", label: t("availability.processing") };
|
||||||
case MediaAvailability.Pending:
|
case MediaAvailability.Pending:
|
||||||
return { color: "violet", label: t("availability.pending") };
|
return { color: "violet", label: t("availability.pending") };
|
||||||
|
case MediaAvailability.Blacklisted:
|
||||||
|
return { color: "gray", label: t("availability.blacklisted") };
|
||||||
|
case MediaAvailability.Deleted:
|
||||||
|
return { color: "red", label: t("availability.deleted") };
|
||||||
default:
|
default:
|
||||||
return { color: "red", label: t("availability.unknown") };
|
return { color: "orange", label: t("availability.unknown") };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useMemo } from "react";
|
||||||
|
import { Card, Flex, Group, ScrollArea, Stack, Text } from "@mantine/core";
|
||||||
|
import { IconClock } from "@tabler/icons-react";
|
||||||
|
|
||||||
|
import { clientApi } from "@homarr/api/client";
|
||||||
|
import { useRequiredBoard } from "@homarr/boards/context";
|
||||||
|
import { useTimeAgo } from "@homarr/common";
|
||||||
|
import { useScopedI18n } from "@homarr/translation/client";
|
||||||
|
|
||||||
|
import type { WidgetComponentProps } from "../definition";
|
||||||
|
|
||||||
|
export default function NotificationsWidget({ options, integrationIds }: WidgetComponentProps<"notifications">) {
|
||||||
|
const [notificationIntegrations] = clientApi.widget.notifications.getNotifications.useSuspenseQuery(
|
||||||
|
{
|
||||||
|
...options,
|
||||||
|
integrationIds,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
refetchOnMount: false,
|
||||||
|
refetchOnWindowFocus: false,
|
||||||
|
refetchOnReconnect: false,
|
||||||
|
retry: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const utils = clientApi.useUtils();
|
||||||
|
|
||||||
|
clientApi.widget.notifications.subscribeNotifications.useSubscription(
|
||||||
|
{
|
||||||
|
...options,
|
||||||
|
integrationIds,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onData: (data) => {
|
||||||
|
utils.widget.notifications.getNotifications.setData({ ...options, integrationIds }, (prevData) => {
|
||||||
|
return prevData?.map((item) => {
|
||||||
|
if (item.integration.id !== data.integration.id) return item;
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: data.data,
|
||||||
|
integration: {
|
||||||
|
...data.integration,
|
||||||
|
updatedAt: new Date(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const t = useScopedI18n("widget.notifications");
|
||||||
|
|
||||||
|
const board = useRequiredBoard();
|
||||||
|
|
||||||
|
const sortedNotifications = useMemo(
|
||||||
|
() =>
|
||||||
|
notificationIntegrations
|
||||||
|
.flatMap((integration) => integration.data)
|
||||||
|
.sort((entryA, entryB) => entryB.time.getTime() - entryA.time.getTime()),
|
||||||
|
[notificationIntegrations],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ScrollArea className="scroll-area-w100" w="100%" p="sm">
|
||||||
|
<Stack w={"100%"} gap="sm">
|
||||||
|
{sortedNotifications.length > 0 ? (
|
||||||
|
sortedNotifications.map((notification) => (
|
||||||
|
<Card key={notification.id} withBorder radius={board.itemRadius} w="100%" p="sm">
|
||||||
|
<Flex gap="sm" direction="column" w="100%">
|
||||||
|
{notification.title && (
|
||||||
|
<Text fz="sm" lh="sm" lineClamp={2}>
|
||||||
|
{notification.title}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
<Text c="dimmed" size="sm" lineClamp={4} style={{ whiteSpace: "pre-line" }}>
|
||||||
|
{notification.body}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<InfoDisplay date={notification.time} />
|
||||||
|
</Flex>
|
||||||
|
</Card>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Text size="sm" c="dimmed">
|
||||||
|
{t("noItems")}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</ScrollArea>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const InfoDisplay = ({ date }: { date: Date }) => {
|
||||||
|
const timeAgo = useTimeAgo(date, 30000); // update every 30sec
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Group gap={5} align={"center"}>
|
||||||
|
<IconClock size={"1rem"} color={"var(--mantine-color-dimmed)"} />
|
||||||
|
<Text size="sm" c="dimmed">
|
||||||
|
{timeAgo}
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { IconMessage } from "@tabler/icons-react";
|
||||||
|
|
||||||
|
import { getIntegrationKindsByCategory } from "@homarr/definitions";
|
||||||
|
|
||||||
|
import { createWidgetDefinition } from "../definition";
|
||||||
|
import { optionsBuilder } from "../options";
|
||||||
|
|
||||||
|
export const { componentLoader, definition } = createWidgetDefinition("notifications", {
|
||||||
|
icon: IconMessage,
|
||||||
|
createOptions() {
|
||||||
|
return optionsBuilder.from(() => ({}));
|
||||||
|
},
|
||||||
|
supportedIntegrations: getIntegrationKindsByCategory("notifications"),
|
||||||
|
}).withDynamicImport(() => import("./component"));
|
||||||
Generated
+758
-569
File diff suppressed because it is too large.
Load diff
@@ -20,11 +20,11 @@
|
|||||||
"@next/eslint-plugin-next": "15.3.4",
|
"@next/eslint-plugin-next": "15.3.4",
|
||||||
"eslint-config-prettier": "^10.1.5",
|
"eslint-config-prettier": "^10.1.5",
|
||||||
"eslint-config-turbo": "^2.5.4",
|
"eslint-config-turbo": "^2.5.4",
|
||||||
"eslint-plugin-import": "^2.31.0",
|
"eslint-plugin-import": "^2.32.0",
|
||||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||||
"eslint-plugin-react": "^7.37.5",
|
"eslint-plugin-react": "^7.37.5",
|
||||||
"eslint-plugin-react-hooks": "^5.2.0",
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
"typescript-eslint": "^8.34.1"
|
"typescript-eslint": "^8.35.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
"prettier": "@homarr/prettier-config",
|
"prettier": "@homarr/prettier-config",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ianvs/prettier-plugin-sort-imports": "^4.4.2",
|
"@ianvs/prettier-plugin-sort-imports": "^4.4.2",
|
||||||
"prettier": "^3.5.3"
|
"prettier": "^3.6.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
|
|||||||
Reference in new issue
Block a user