Implement log streaming with subscription-based approach instead of polling

Co-authored-by: ajnart <49837342+ajnart@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]andajnart committed 2026-01-06 19:57:33 +00:00
1 parent 762cdab78f
commit 23ce0d14ae
3 files changed
+156 -18

No files matched your search

@@ -4,7 +4,11 @@ import { z } from "zod/v4";
import type { Container, ContainerState, Docker, Port } from "@homarr/docker";
import { DockerSingleton } from "@homarr/docker";
import { dockerContainersRequestHandler, getContainerLogsAsync } from "@homarr/request-handler/docker";
import {
dockerContainersRequestHandler,
getContainerLogsAsync,
streamContainerLogsAsync,
} from "@homarr/request-handler/docker";
import { dockerMiddleware } from "../../middlewares/docker";
import { createTRPCRouter, permissionRequiredProcedure } from "../../trpc";
@@ -127,6 +131,51 @@ export const dockerRouter = createTRPCRouter({
logs,
};
}),
subscribeLogs: permissionRequiredProcedure
.requiresPermission("admin")
.concat(dockerMiddleware())
.input(
z.object({
id: z.string(),
tail: z.number().min(1).max(1000).optional(),
}),
)
.subscription(({ input }) => {
return observable<string>((emit) => {
let cleanup: (() => void) | undefined;
streamContainerLogsAsync(
input.id,
input.tail ?? 200,
(data) => {
emit.next(data);
},
(err) => {
emit.error(
new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: err.message,
}),
);
},
)
.then((cleanupFn) => {
cleanup = cleanupFn;
})
.catch((err) => {
emit.error(
new TRPCError({
code: "NOT_FOUND",
message: err instanceof Error ? err.message : "Container not found",
}),
);
});
return () => {
cleanup?.();
};
});
}),
});
const getContainerOrDefaultAsync = async (instance: Docker, id: string) => {