fix: remove unnecessary demo and docker handling
This commit is contained in:
1 parent
809eed8ef9
commit
a935fd78fb
12 files changed
+16
-159
No files matched your search
@@ -36,7 +36,7 @@ pnpm cli harness setup feature-board
|
||||
|
||||
The command builds `homarr:feature-board`, starts a container with a persistent data volume, enables demo mode, and keeps demo mutations writable (`DEMO_READ_ONLY=false`). Docker assigns a free host port by default; use the URL printed by the command or query it later:
|
||||
|
||||
The demo board includes every widget kind. Integration-backed widgets use the bundled mock integration; media widgets use bundled same-origin demo assets.
|
||||
The demo board includes every widget kind. Integration-backed widgets use the bundled mock integration; media widgets use public stock assets.
|
||||
|
||||
```sh
|
||||
pnpm cli harness port feature-board
|
||||
|
||||
@@ -144,10 +144,6 @@ Docker or Podman endpoints from targeting the wrong service.
|
||||
Docker environment, or pass one or more IDs returned by `docker.getEndpoints` to query only those Dockerode clients. The
|
||||
Docker widget uses the same behavior for its environment selector.
|
||||
|
||||
`docker.refreshInventory` always refreshes the current replica and returns `scope: "published"` when Redis accepts the
|
||||
non-persistent refresh event, or `scope: "local"` when publication fails. Published scope does not confirm that every replica
|
||||
processed the event. A local result is partial success: retry after restoring Redis to notify the other replicas.
|
||||
|
||||
## Partial integration failures
|
||||
|
||||
Multi-integration widget queries keep data from integrations that remain available. Calendar and media-server results include
|
||||
|
||||
@@ -41,7 +41,7 @@ import type { UrlTemplateMode } from "@homarr/definitions";
|
||||
import { getIntegrationName, invariantTechnicalLabels } from "@homarr/definitions";
|
||||
import { useModalAction } from "@homarr/modals";
|
||||
import { AddDockerAppToHomarr } from "@homarr/modals-collection";
|
||||
import { showErrorNotification, showWarningNotification } from "@homarr/notifications";
|
||||
import { showErrorNotification } from "@homarr/notifications";
|
||||
import { normalizeServiceUrl, ServiceUrlTemplate } from "@homarr/onboarding";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { Link } from "@homarr/ui";
|
||||
@@ -69,18 +69,12 @@ export const DockerReconciliation = ({ defaultServerOrigin }: { defaultServerOri
|
||||
const reconciliation = clientApi.docker.reconcileServices.useQuery();
|
||||
const health = clientApi.docker.getServiceHealth.useQuery(undefined, { enabled: isOpen });
|
||||
const refreshInventory = clientApi.docker.refreshInventory.useMutation({
|
||||
async onSuccess(result) {
|
||||
async onSuccess() {
|
||||
await Promise.all([
|
||||
utils.docker.getContainers.invalidate(),
|
||||
utils.docker.reconcileServices.invalidate(),
|
||||
utils.docker.getServiceHealth.invalidate(),
|
||||
]);
|
||||
if (result.scope === "local") {
|
||||
showWarningNotification({
|
||||
title: tDockerAction("refresh.notification.warning.title"),
|
||||
message: tDockerAction("refresh.notification.warning.message"),
|
||||
});
|
||||
}
|
||||
},
|
||||
onError() {
|
||||
showErrorNotification({
|
||||
|
||||
@@ -25,7 +25,7 @@ import type { ContainerState, DockerEndpointCapability } from "@homarr/docker";
|
||||
import { containerStateColorMap, cpuUsageColor, memoryUsageColor, safeValue } from "@homarr/docker/shared";
|
||||
import { useModalAction } from "@homarr/modals";
|
||||
import { AddDockerAppToHomarr, useDockerContainerRemovalConfirmation } from "@homarr/modals-collection";
|
||||
import { showErrorNotification, showSuccessNotification, showWarningNotification } from "@homarr/notifications";
|
||||
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
|
||||
import type { ScopedTranslationFunction } from "@homarr/translation";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import type { TablerIcon } from "@homarr/ui";
|
||||
@@ -199,19 +199,12 @@ export function DockerTable({ initialData }: DockerTableProps) {
|
||||
refetchOnMount: false,
|
||||
});
|
||||
const refreshInventory = clientApi.docker.refreshInventory.useMutation({
|
||||
async onSuccess(result) {
|
||||
async onSuccess() {
|
||||
await Promise.all([
|
||||
utils.docker.getContainers.invalidate(),
|
||||
utils.docker.reconcileServices.invalidate(),
|
||||
utils.docker.getServiceHealth.invalidate(),
|
||||
]);
|
||||
if (result.scope === "local") {
|
||||
showWarningNotification({
|
||||
title: tDocker("action.refresh.notification.warning.title"),
|
||||
message: tDocker("action.refresh.notification.warning.message"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
showSuccessNotification({
|
||||
title: tDocker("action.refresh.notification.success.title"),
|
||||
message: tDocker("action.refresh.notification.success.message"),
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
const overview = `<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
overflow: hidden;
|
||||
color: #f8f9fa;
|
||||
font-family: Inter, ui-sans-serif, system-ui, sans-serif;
|
||||
background:
|
||||
radial-gradient(circle at 18% 15%, rgba(116, 143, 252, .32), transparent 36%),
|
||||
radial-gradient(circle at 82% 80%, rgba(59, 201, 219, .22), transparent 42%),
|
||||
#15121f;
|
||||
}
|
||||
main { width: min(86%, 34rem); text-align: center; }
|
||||
.mark {
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
margin: 0 auto 1rem;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border-radius: 1.25rem;
|
||||
font-size: 1.75rem;
|
||||
font-weight: 900;
|
||||
background: linear-gradient(135deg, #748ffc, #3bc9db);
|
||||
box-shadow: 0 1rem 3rem rgba(116, 143, 252, .34);
|
||||
}
|
||||
h1 { margin: 0; font-size: clamp(1.5rem, 8vw, 2.5rem); letter-spacing: -.04em; }
|
||||
p { margin: .65rem auto 0; max-width: 28rem; color: #c1c2c5; line-height: 1.55; }
|
||||
.signals { display: flex; justify-content: center; gap: .55rem; margin-top: 1.25rem; flex-wrap: wrap; }
|
||||
span { padding: .38rem .7rem; border: 1px solid rgba(255,255,255,.13); border-radius: 999px; background: rgba(255,255,255,.06); font-size: .75rem; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<div class="mark">H</div>
|
||||
<h1>Your home, at a glance.</h1>
|
||||
<p>Bring services, media, infrastructure, notes and everyday tools together on one practical dashboard.</p>
|
||||
<div class="signals"><span>59 widgets</span><span>Mock data included</span><span>Built for self-hosters</span></div>
|
||||
</main>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
export const GET = () =>
|
||||
new Response(overview, {
|
||||
headers: {
|
||||
"Cache-Control": "public, max-age=3600",
|
||||
"Content-Type": "text/html; charset=utf-8",
|
||||
},
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
export { GET } from "../route";
|
||||
@@ -1,10 +0,0 @@
|
||||
const videoBase64 =
|
||||
"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
Line truncated
|
||||
|
||||
export const GET = () =>
|
||||
new Response(Buffer.from(videoBase64, "base64"), {
|
||||
headers: {
|
||||
"Cache-Control": "public, max-age=3600",
|
||||
"Content-Type": "video/mp4",
|
||||
},
|
||||
});
|
||||
@@ -4,8 +4,6 @@ import { z } from "zod/v4";
|
||||
|
||||
import type { ContainerState, DockerContainerTarget, Port } from "@homarr/docker";
|
||||
import { DockerSingleton } from "@homarr/docker";
|
||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
||||
import { createSubPubChannel } from "@homarr/redis";
|
||||
import {
|
||||
dockerContainersRequestHandler,
|
||||
findDockerContainerAsync,
|
||||
@@ -24,34 +22,6 @@ const dockerContainerTargetSchema = z.object({
|
||||
id: z.string().min(1),
|
||||
});
|
||||
const dockerContainerTargetsSchema = z.array(dockerContainerTargetSchema).min(1).max(100);
|
||||
const logger = createLogger({ module: "dockerRouter" });
|
||||
|
||||
const resetDockerInventory = () => {
|
||||
DockerSingleton.reset();
|
||||
dockerContainersRequestHandler.invalidateCache();
|
||||
};
|
||||
|
||||
const dockerInventoryRefreshChannel = createSubPubChannel<{ requestedAt: number }>("docker-inventory-refresh", {
|
||||
persist: false,
|
||||
});
|
||||
const isRedisDisabled = () => Boolean(process.env.CI) || Boolean(process.env.DISABLE_REDIS_LOGS);
|
||||
try {
|
||||
dockerInventoryRefreshChannel.subscribe(resetDockerInventory);
|
||||
} catch (error) {
|
||||
if (!isRedisDisabled()) throw error;
|
||||
}
|
||||
|
||||
const publishDockerInventoryRefreshAsync = async () => {
|
||||
try {
|
||||
await dockerInventoryRefreshChannel.publishAsync({ requestedAt: Date.now() });
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (!isRedisDisabled()) {
|
||||
logger.warn(new Error("Failed to publish Docker inventory refresh", { cause: error }));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const normalizeGetContainersInput = (input: { endpointIds?: string[] } | undefined) => {
|
||||
if (!input?.endpointIds || input.endpointIds.length === 0) return {};
|
||||
@@ -87,15 +57,13 @@ export const dockerRouter = createTRPCRouter({
|
||||
mcp: {
|
||||
enabled: true,
|
||||
description:
|
||||
"Invalidate the local Docker inventory and publish a non-persistent Redis refresh event. Returns scope 'published' when Redis accepts the event or 'local' when only this replica is refreshed. Requires admin permission.",
|
||||
"Invalidate the cached Docker inventory so the next Docker query performs a fresh user-requested discovery. Requires admin permission.",
|
||||
},
|
||||
})
|
||||
.concat(dockerMiddleware())
|
||||
.mutation(async () => {
|
||||
resetDockerInventory();
|
||||
const propagated = await publishDockerInventoryRefreshAsync();
|
||||
if (propagated) return { scope: "published" as const };
|
||||
return { scope: "local" as const };
|
||||
.mutation(() => {
|
||||
DockerSingleton.reset();
|
||||
dockerContainersRequestHandler.invalidateCache();
|
||||
}),
|
||||
getContainers: permissionRequiredProcedure
|
||||
.requiresPermission("admin")
|
||||
|
||||
@@ -15,17 +15,6 @@ const findDockerContainerAsyncMock = vi.hoisted(() => vi.fn());
|
||||
const hasDockerEndpointCapabilityMock = vi.hoisted(() => vi.fn(() => true));
|
||||
const invalidateDockerCacheMock = vi.hoisted(() => vi.fn());
|
||||
const resetDockerSingletonMock = vi.hoisted(() => vi.fn());
|
||||
const dockerInventoryRefreshChannelMock = vi.hoisted(() => {
|
||||
let subscriber: (() => void) | undefined;
|
||||
return {
|
||||
publishAsync: vi.fn(async () => undefined),
|
||||
subscribe: vi.fn((callback: () => void) => {
|
||||
subscriber = callback;
|
||||
return () => undefined;
|
||||
}),
|
||||
trigger: () => subscriber?.(),
|
||||
};
|
||||
});
|
||||
const getDockerDataAsyncMock = vi.hoisted(() =>
|
||||
vi.fn<
|
||||
() => Promise<{
|
||||
@@ -77,9 +66,6 @@ vi.mock("@homarr/docker", () => ({
|
||||
reset: resetDockerSingletonMock,
|
||||
},
|
||||
}));
|
||||
vi.mock("@homarr/redis", () => ({
|
||||
createSubPubChannel: () => dockerInventoryRefreshChannelMock,
|
||||
}));
|
||||
vi.mock("@homarr/request-handler/docker", () => ({
|
||||
findDockerContainerAsync: findDockerContainerAsyncMock,
|
||||
hasDockerEndpointCapability: hasDockerEndpointCapabilityMock,
|
||||
@@ -517,12 +503,6 @@ test("invalidates the shared Docker inventory on explicit refresh", async () =>
|
||||
|
||||
expect(invalidateDockerCacheMock).toHaveBeenCalledOnce();
|
||||
expect(resetDockerSingletonMock).toHaveBeenCalledOnce();
|
||||
expect(dockerInventoryRefreshChannelMock.publishAsync).toHaveBeenCalledOnce();
|
||||
|
||||
dockerInventoryRefreshChannelMock.trigger();
|
||||
|
||||
expect(invalidateDockerCacheMock).toHaveBeenCalledTimes(2);
|
||||
expect(resetDockerSingletonMock).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
test("projects persisted service layers without inventing runtime health", async () => {
|
||||
|
||||
@@ -805,7 +805,10 @@ const buildDemoWidgets = (appIds: string[], customWidgetDefinitionId: string): D
|
||||
width: 4,
|
||||
height: 3,
|
||||
needsIntegration: false,
|
||||
options: { embedUrl: "/api/demo-assets/overview", allowScrolling: true },
|
||||
options: {
|
||||
embedUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/homarr.svg",
|
||||
allowScrolling: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: "video",
|
||||
@@ -815,7 +818,7 @@ const buildDemoWidgets = (appIds: string[], customWidgetDefinitionId: string): D
|
||||
height: 3,
|
||||
needsIntegration: false,
|
||||
options: {
|
||||
feedUrl: "/api/demo-assets/video/file.mp4",
|
||||
feedUrl: "https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4",
|
||||
hasAutoPlay: false,
|
||||
isMuted: true,
|
||||
hasControls: true,
|
||||
|
||||
@@ -6644,10 +6644,6 @@
|
||||
"title": "Containers refreshed",
|
||||
"message": "You are now viewing the most recent data"
|
||||
},
|
||||
"warning": {
|
||||
"title": "Only this Homarr instance was refreshed",
|
||||
"message": "Redis did not accept the refresh event, so other Homarr instances may still show older Docker data"
|
||||
},
|
||||
"error": {
|
||||
"title": "Containers not refreshed",
|
||||
"message": "Something went wrong while refreshing the containers"
|
||||
|
||||
@@ -42,7 +42,7 @@ import type { ContainerState, DockerEndpointCapability } from "@homarr/docker";
|
||||
import { containerStateColorMap, cpuUsageColor, memoryUsageColor, safeValue } from "@homarr/docker/shared";
|
||||
import { useModalAction } from "@homarr/modals";
|
||||
import { AddDockerAppToHomarr, useDockerContainerRemovalConfirmation } from "@homarr/modals-collection";
|
||||
import { showErrorNotification, showSuccessNotification, showWarningNotification } from "@homarr/notifications";
|
||||
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { zoomCompensatedSize } from "@homarr/ui";
|
||||
|
||||
@@ -285,18 +285,12 @@ export default function DockerWidget({
|
||||
const data = getUsableWidgetQueryData(containersQuery);
|
||||
const { isFetching } = containersQuery;
|
||||
const refreshInventory = clientApi.docker.refreshInventory.useMutation({
|
||||
async onSuccess(result) {
|
||||
async onSuccess() {
|
||||
await Promise.all([
|
||||
utils.docker.getContainers.invalidate(),
|
||||
utils.docker.reconcileServices.invalidate(),
|
||||
utils.docker.getServiceHealth.invalidate(),
|
||||
]);
|
||||
if (result.scope === "local") {
|
||||
showWarningNotification({
|
||||
title: t("action.refresh.notification.warning.title"),
|
||||
message: t("action.refresh.notification.warning.message"),
|
||||
});
|
||||
}
|
||||
},
|
||||
onError() {
|
||||
showErrorNotification({
|
||||
|
||||
Reference in new issue
Block a user