refactor: align cluster/swarm read queries with docker.read, gate node actions by server permissions

This commit is contained in:
Mauricio Siu
2026-08-11 02:04:09 -06:00
parent c76c5b2f90
commit 4732b38d5a
3 changed files with 35 additions and 33 deletions
@@ -52,6 +52,7 @@ export const ShowNodes = ({ serverId }: Props) => {
serverId,
});
const { data: registry } = api.registry.all.useQuery();
const { data: permissions } = api.user.getPermissions.useQuery();
const { mutateAsync: deleteNode } = api.cluster.removeWorker.useMutation();
@@ -68,7 +69,7 @@ export const ShowNodes = ({ serverId }: Props) => {
</CardTitle>
<CardDescription>Add nodes to your cluster</CardDescription>
</div>
{haveAtLeastOneRegistry && (
{haveAtLeastOneRegistry && permissions?.server.create && (
<div className="flex flex-row gap-2">
<AddNode serverId={serverId} />
</div>
@@ -144,34 +145,35 @@ export const ShowNodes = ({ serverId }: Props) => {
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<ShowNodeData data={node} />
{!node?.ManagerStatus?.Leader && (
<DialogAction
title="Delete Node"
description="Are you sure you want to delete this node from the cluster?"
type="destructive"
onClick={async () => {
await deleteNode({
nodeId: node.ID,
serverId,
})
.then(() => {
refetch();
toast.success(
"Node deleted successfully",
);
{!node?.ManagerStatus?.Leader &&
permissions?.server.delete && (
<DialogAction
title="Delete Node"
description="Are you sure you want to delete this node from the cluster?"
type="destructive"
onClick={async () => {
await deleteNode({
nodeId: node.ID,
serverId,
})
.catch(() => {
toast.error("Error deleting node");
});
}}
>
<DropdownMenuItem
onSelect={(e) => e.preventDefault()}
.then(() => {
refetch();
toast.success(
"Node deleted successfully",
);
})
.catch(() => {
toast.error("Error deleting node");
});
}}
>
Delete
</DropdownMenuItem>
</DialogAction>
)}
<DropdownMenuItem
onSelect={(e) => e.preventDefault()}
>
Delete
</DropdownMenuItem>
</DialogAction>
)}
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
+1 -1
View File
@@ -13,7 +13,7 @@ import { getLocalServerIp } from "@/server/wss/terminal";
import { createTRPCRouter, withPermission } from "../trpc";
export const clusterRouter = createTRPCRouter({
getNodes: withPermission("server", "read")
getNodes: withPermission("docker", "read")
.input(
z.object({
serverId: z.string().optional(),
+5 -5
View File
@@ -12,7 +12,7 @@ import { createTRPCRouter, withPermission } from "../trpc";
import { containerIdRegex } from "./docker";
export const swarmRouter = createTRPCRouter({
getNodes: withPermission("server", "read")
getNodes: withPermission("docker", "read")
.input(
z.object({
serverId: z.string().optional(),
@@ -30,7 +30,7 @@ export const swarmRouter = createTRPCRouter({
}
return await getSwarmNodes(input.serverId);
}),
getNodeInfo: withPermission("server", "read")
getNodeInfo: withPermission("docker", "read")
.input(z.object({ nodeId: z.string(), serverId: z.string().optional() }))
.query(async ({ input, ctx }) => {
if (input.serverId) {
@@ -44,7 +44,7 @@ export const swarmRouter = createTRPCRouter({
}
return await getNodeInfo(input.nodeId, input.serverId);
}),
getNodeApps: withPermission("server", "read")
getNodeApps: withPermission("docker", "read")
.input(
z.object({
serverId: z.string().optional(),
@@ -62,7 +62,7 @@ export const swarmRouter = createTRPCRouter({
}
return getNodeApplications(input.serverId);
}),
getAppInfos: withPermission("server", "read")
getAppInfos: withPermission("docker", "read")
.meta({
openapi: {
path: "/drop-deployment",
@@ -93,7 +93,7 @@ export const swarmRouter = createTRPCRouter({
}
return await getApplicationInfo(input.appName, input.serverId);
}),
getContainerStats: withPermission("server", "read")
getContainerStats: withPermission("docker", "read")
.input(
z.object({
serverId: z.string().optional(),