Skip to content

Commit e4cdc8f

Browse files
committed
feat: show compose volumes
1 parent 068deec commit e4cdc8f

2 files changed

Lines changed: 114 additions & 7 deletions

File tree

apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ export const ShowVolumes = ({ id, type }: Props) => {
3737
const { data, refetch } = queryMap[type]
3838
? queryMap[type]()
3939
: api.mongo.one.useQuery({ mongoId: id }, { enabled: !!id });
40+
41+
// Get volumes in docker-compose.yml for compose services
42+
const { data: composeVolumes } = api.compose.loadDefinedVolumes.useQuery(
43+
{ composeId: id },
44+
{ enabled: type === "compose" && !!id }
45+
);
46+
4047
const { mutateAsync: deleteVolume, isLoading: isRemoving } =
4148
api.mounts.remove.useMutation();
4249
return (
@@ -50,14 +57,15 @@ export const ShowVolumes = ({ id, type }: Props) => {
5057
</CardDescription>
5158
</div>
5259

53-
{data && data?.mounts.length > 0 && (
60+
{data && data?.mounts?.length > 0 && (
5461
<AddVolumes serviceId={id} refetch={refetch} serviceType={type}>
5562
Add Volume
5663
</AddVolumes>
5764
)}
5865
</CardHeader>
5966
<CardContent className="flex flex-col gap-4">
60-
{data?.mounts.length === 0 ? (
67+
{data?.mounts?.length === 0 &&
68+
(type !== "compose" || !composeVolumes || Object.keys(composeVolumes).length === 0) ? (
6169
<div className="flex w-full flex-col items-center justify-center gap-3 pt-10">
6270
<Package className="size-8 text-muted-foreground" />
6371
<span className="text-base text-muted-foreground">
@@ -69,12 +77,24 @@ export const ShowVolumes = ({ id, type }: Props) => {
6977
</div>
7078
) : (
7179
<div className="flex flex-col pt-2 gap-4">
72-
<AlertBlock type="warning">
73-
Please remember to click Redeploy after adding, editing, or
74-
deleting a mount to apply the changes.
75-
</AlertBlock>
80+
{data?.mounts?.length > 0 && (
81+
<AlertBlock type="warning">
82+
Please remember to click Redeploy after adding, editing, or
83+
deleting a mount to apply the changes.
84+
</AlertBlock>
85+
)}
86+
{data?.mounts?.length > 0 && type === "compose" && composeVolumes && Object.keys(composeVolumes).length > 0 && (
87+
<div className="border-t pt-4">
88+
<div>
89+
<h3 className="text-lg font-semibold">File Mounts</h3>
90+
<p className="text-sm text-muted-foreground">
91+
File mounts configured through Dokploy interface
92+
</p>
93+
</div>
94+
</div>
95+
)}
7696
<div className="flex flex-col gap-6">
77-
{data?.mounts.map((mount) => (
97+
{data?.mounts?.map((mount) => (
7898
<div key={mount.mountId}>
7999
<div
80100
key={mount.mountId}
@@ -169,6 +189,49 @@ export const ShowVolumes = ({ id, type }: Props) => {
169189
</div>
170190
</div>
171191
)}
192+
{/* Show defined volumes from docker-compose.yml for compose services */}
193+
{type === "compose" && composeVolumes && Object.keys(composeVolumes).length > 0 && (
194+
<div className="space-y-4">
195+
<div>
196+
<h3 className="text-lg font-semibold">Defined Volumes</h3>
197+
<p className="text-sm text-muted-foreground">
198+
Volumes defined in your docker-compose.yml file
199+
</p>
200+
</div>
201+
<div className="grid gap-3">
202+
{Object.entries(composeVolumes).map(([volumeName, volumeConfig]) => (
203+
<div key={volumeName} className="border rounded-lg p-4">
204+
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
205+
<div className="flex flex-col gap-1">
206+
<span className="font-medium">Volume Name</span>
207+
<span className="text-sm text-muted-foreground">
208+
{volumeName}
209+
</span>
210+
</div>
211+
<div className="flex flex-col gap-1">
212+
<span className="font-medium">Driver</span>
213+
<span className="text-sm text-muted-foreground">
214+
{typeof volumeConfig === 'object' && volumeConfig !== null
215+
? (volumeConfig as any)?.driver || 'default'
216+
: 'default'
217+
}
218+
</span>
219+
</div>
220+
<div className="flex flex-col gap-1">
221+
<span className="font-medium">External</span>
222+
<span className="text-sm text-muted-foreground">
223+
{typeof volumeConfig === 'object' && volumeConfig !== null
224+
? (volumeConfig as any)?.external ? 'Yes' : 'No'
225+
: 'No'
226+
}
227+
</span>
228+
</div>
229+
</div>
230+
</div>
231+
))}
232+
</div>
233+
</div>
234+
)}
172235
</CardContent>
173236
</Card>
174237
);

apps/dokploy/server/api/routers/compose.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import {
2020
getComposeContainer,
2121
getWebServerSettings,
2222
IS_CLOUD,
23+
loadDockerCompose,
24+
loadDockerComposeRemote,
2325
loadServices,
2426
randomizeComposeFile,
2527
randomizeIsolatedDeploymentComposeFile,
@@ -339,6 +341,48 @@ export const composeRouter = createTRPCRouter({
339341
}
340342
}),
341343

344+
/**
345+
* Load volumes defined in the docker-compose file
346+
*/
347+
loadDefinedVolumes: protectedProcedure
348+
.input(apiFindCompose)
349+
.query(async ({ input, ctx }) => {
350+
const compose = await findComposeById(input.composeId);
351+
352+
if (
353+
compose.environment.project.organizationId !==
354+
ctx.session.activeOrganizationId
355+
) {
356+
throw new TRPCError({
357+
code: "UNAUTHORIZED",
358+
message: "You are not authorized to load this compose",
359+
});
360+
}
361+
362+
try {
363+
// Clone and load the docker-compose file
364+
const command = await cloneCompose(compose);
365+
if (compose.serverId) {
366+
await execAsyncRemote(compose.serverId, command);
367+
} else {
368+
await execAsync(command);
369+
}
370+
371+
// Load and parse the docker-compose.yml file
372+
let composeData;
373+
if (compose.serverId) {
374+
composeData = await loadDockerComposeRemote(compose);
375+
} else {
376+
composeData = await loadDockerCompose(compose);
377+
}
378+
379+
return composeData?.volumes || {};
380+
} catch (err) {
381+
console.error("Error loading defined volumes:", err);
382+
return {};
383+
}
384+
}),
385+
342386
randomizeCompose: protectedProcedure
343387
.input(apiRandomizeCompose)
344388
.mutation(async ({ input, ctx }) => {

0 commit comments

Comments
 (0)