Skip to content

Conversation

@krokodaws
Copy link
Contributor

Update bulk action endpoints for database services:

  • Use /api/trpc/redis.start and /api/trpc/redis.stop for Redis
  • Use /api/trpc/postgres.start and /api/trpc/postgres.stop for PostgreSQL
  • Retain /api/trpc/compose.start and /api/trpc/compose.stop for Docker Compose services

Tested with a project including Gitea, Redis, and PostgreSQL. Bulk start/stop operations now function correctly for all service types.

Closes #1626

)

Update bulk action endpoints for database services:
- Use `/api/trpc/redis.start` and `/api/trpc/redis.stop` for Redis
- Use `/api/trpc/postgres.start` and `/api/trpc/postgres.stop` for PostgreSQL
- Retain `/api/trpc/compose.start` and `/api/trpc/compose.stop` for Docker Compose services

Tested with a project including Gitea, Redis, and PostgreSQL. Bulk start/stop operations now function correctly for all service types.

Closes Dokploy#1626
@krokodaws krokodaws requested a review from Siumauricio as a code owner April 4, 2025 16:28
@gnpaone
Copy link

gnpaone commented Apr 5, 2025

Hey @krokodaws please check this too krokodaws#1 I have refactored code to make it simpler. It's a staggered PR so needed to merge krokodaws#1 into #1629

Comment on lines 352 to 582
const handleBulkAction = async (
action: "start" | "stop" | "move" | "delete",
extraParams: { targetProjectId?: string } = {},
) => {
let success = 0;
setIsBulkActionLoading(true);
for (const serviceId of selectedServices) {
try {
await composeActions.start.mutateAsync({ composeId: serviceId });
success++;
} catch (_error) {
toast.error(`Error starting service ${serviceId}`);
}
}
if (success > 0) {
toast.success(`${success} services started successfully`);
refetch();
}
setIsBulkActionLoading(false);
setSelectedServices([]);
setIsDropdownOpen(false);
};

const handleBulkStop = async () => {
let success = 0;
setIsBulkActionLoading(true);
for (const serviceId of selectedServices) {
try {
await composeActions.stop.mutateAsync({ composeId: serviceId });
success++;
} catch (_error) {
toast.error(`Error stopping service ${serviceId}`);
}
}
if (success > 0) {
toast.success(`${success} services stopped successfully`);
refetch();
}
setSelectedServices([]);
setIsDropdownOpen(false);
setIsBulkActionLoading(false);
};
const service = filteredServices.find((s) => s.id === serviceId);
if (!service) continue;

const handleBulkMove = async () => {
if (!selectedTargetProject) {
toast.error("Please select a target project");
return;
}
const actions =
serviceActionsMap[service.type as keyof typeof serviceActionsMap];
const mutation = actions?.[action];

if (!mutation) continue;

let success = 0;
setIsBulkActionLoading(true);
for (const serviceId of selectedServices) {
try {
const service = filteredServices.find((s) => s.id === serviceId);
if (!service) continue;
const payload: Record<string, any> = {
[`${service.type}Id`]: serviceId,
};

switch (service.type) {
case "application":
await applicationActions.move.mutateAsync({
applicationId: serviceId,
targetProjectId: selectedTargetProject,
});
break;
case "compose":
await composeActions.move.mutateAsync({
composeId: serviceId,
targetProjectId: selectedTargetProject,
});
break;
case "postgres":
await postgresActions.move.mutateAsync({
postgresId: serviceId,
targetProjectId: selectedTargetProject,
});
break;
case "mysql":
await mysqlActions.move.mutateAsync({
mysqlId: serviceId,
targetProjectId: selectedTargetProject,
});
break;
case "mariadb":
await mariadbActions.move.mutateAsync({
mariadbId: serviceId,
targetProjectId: selectedTargetProject,
});
break;
case "redis":
await redisActions.move.mutateAsync({
redisId: serviceId,
targetProjectId: selectedTargetProject,
});
break;
case "mongo":
await mongoActions.move.mutateAsync({
mongoId: serviceId,
targetProjectId: selectedTargetProject,
});
break;
if (action === "move" && extraParams.targetProjectId) {
payload.targetProjectId = extraParams.targetProjectId;
}
success++;
} catch (error) {
toast.error(
`Error moving service ${serviceId}: ${error instanceof Error ? error.message : "Unknown error"}`,
);
}
}
if (success > 0) {
toast.success(`${success} services moved successfully`);
refetch();
}
setSelectedServices([]);
setIsDropdownOpen(false);
setIsMoveDialogOpen(false);
setIsBulkActionLoading(false);
};

const handleBulkDelete = async () => {
let success = 0;
setIsBulkActionLoading(true);
for (const serviceId of selectedServices) {
try {
const service = filteredServices.find((s) => s.id === serviceId);
if (!service) continue;

switch (service.type) {
case "application":
await applicationActions.delete.mutateAsync({
applicationId: serviceId,
});
break;
case "compose":
await composeActions.delete.mutateAsync({
composeId: serviceId,
deleteVolumes: false,
});
break;
case "postgres":
await postgresActions.delete.mutateAsync({
postgresId: serviceId,
});
break;
case "mysql":
await mysqlActions.delete.mutateAsync({
mysqlId: serviceId,
});
break;
case "mariadb":
await mariadbActions.delete.mutateAsync({
mariadbId: serviceId,
});
break;
case "redis":
await redisActions.delete.mutateAsync({
redisId: serviceId,
});
break;
case "mongo":
await mongoActions.delete.mutateAsync({
mongoId: serviceId,
});
break;
if (action === "delete" && service.type === "compose") {
payload.deleteVolumes = false;
}

await mutation.mutateAsync(payload);
success++;
} catch (error) {
toast.error(
`Error deleting service ${serviceId}: ${error instanceof Error ? error.message : "Unknown error"}`,
);
toast.error(`Error during ${action} of service ${serviceId}`);
}
}

if (success > 0) {
toast.success(`${success} services deleted successfully`);
toast.success(`${success} services ${action}ed successfully`);
refetch();
}
setIsBulkActionLoading(false);
setSelectedServices([]);
setIsDropdownOpen(false);
setIsBulkActionLoading(false);
if (action === "move") setIsMoveDialogOpen(false);
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather have the way we had it, I feel it's easier to add and modify, than a method that is somewhat complex

Could you simply add the missing mutations to each object in the service?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I've returned all the changes to their original form.

@krokodaws krokodaws requested a review from Siumauricio April 6, 2025 13:04
@Siumauricio Siumauricio merged commit 61a20f1 into Dokploy:canary Apr 6, 2025
@krokodaws krokodaws deleted the fix/bulk-actions branch April 6, 2025 22:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bulk Actions Use Incorrect Endpoints for Database Services (PostgreSQL, Redis)

3 participants