feat(backup): enhance RestoreBackup component and API to include serverId

- Added serverId prop to RestoreBackup component for better context during backup restoration.
- Updated ShowBackups component to pass serverId from the Postgres object.
- Modified backup API to handle serverId, allowing remote execution of backup commands when specified.
- Improved file display in RestoreBackup for better user experience.
This commit is contained in:
Mauricio Siu
2025-03-18 00:47:50 -06:00
parent 4fa5e10789
commit ea6cfc9d29
3 changed files with 35 additions and 6 deletions

View File

@@ -48,6 +48,7 @@ import { toast } from "sonner";
interface Props { interface Props {
databaseId: string; databaseId: string;
databaseType: Exclude<ServiceType, "application" | "redis">; databaseType: Exclude<ServiceType, "application" | "redis">;
serverId: string | null;
} }
const RestoreBackupSchema = z.object({ const RestoreBackupSchema = z.object({
@@ -76,7 +77,11 @@ const RestoreBackupSchema = z.object({
type RestoreBackup = z.infer<typeof RestoreBackupSchema>; type RestoreBackup = z.infer<typeof RestoreBackupSchema>;
export const RestoreBackup = ({ databaseId, databaseType }: Props) => { export const RestoreBackup = ({
databaseId,
databaseType,
serverId,
}: Props) => {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");
@@ -101,6 +106,7 @@ export const RestoreBackup = ({ databaseId, databaseType }: Props) => {
{ {
destinationId: destionationId, destinationId: destionationId,
search, search,
serverId: serverId ?? "",
}, },
{ {
enabled: isOpen && !!destionationId, enabled: isOpen && !!destionationId,
@@ -304,7 +310,9 @@ export const RestoreBackup = ({ databaseId, databaseType }: Props) => {
form.setValue("backupFile", file); form.setValue("backupFile", file);
}} }}
> >
{file} <div className="flex w-full justify-between">
<span>{file}</span>
</div>
<CheckIcon <CheckIcon
className={cn( className={cn(
"ml-auto h-4 w-4", "ml-auto h-4 w-4",

View File

@@ -74,7 +74,11 @@ export const ShowBackups = ({ id, type }: Props) => {
{postgres && postgres?.backups?.length > 0 && ( {postgres && postgres?.backups?.length > 0 && (
<div className="flex flex-col lg:flex-row gap-4 w-full lg:w-auto"> <div className="flex flex-col lg:flex-row gap-4 w-full lg:w-auto">
<AddBackup databaseId={id} databaseType={type} refetch={refetch} /> <AddBackup databaseId={id} databaseType={type} refetch={refetch} />
<RestoreBackup databaseId={id} databaseType={type} /> <RestoreBackup
databaseId={id}
databaseType={type}
serverId={postgres.serverId}
/>
</div> </div>
)} )}
</CardHeader> </CardHeader>
@@ -108,7 +112,11 @@ export const ShowBackups = ({ id, type }: Props) => {
databaseType={type} databaseType={type}
refetch={refetch} refetch={refetch}
/> />
<RestoreBackup databaseId={id} databaseType={type} /> <RestoreBackup
databaseId={id}
databaseType={type}
serverId={postgres.serverId}
/>
</div> </div>
</div> </div>
) : ( ) : (

View File

@@ -31,7 +31,10 @@ import {
import { TRPCError } from "@trpc/server"; import { TRPCError } from "@trpc/server";
import { z } from "zod"; import { z } from "zod";
import { execAsync } from "@dokploy/server/utils/process/execAsync"; import {
execAsync,
execAsyncRemote,
} from "@dokploy/server/utils/process/execAsync";
import { getS3Credentials } from "@dokploy/server/utils/backups/utils"; import { getS3Credentials } from "@dokploy/server/utils/backups/utils";
import { findDestinationById } from "@dokploy/server/services/destination"; import { findDestinationById } from "@dokploy/server/services/destination";
import { import {
@@ -229,6 +232,7 @@ export const backupRouter = createTRPCRouter({
z.object({ z.object({
destinationId: z.string(), destinationId: z.string(),
search: z.string(), search: z.string(),
serverId: z.string().optional(),
}), }),
) )
.query(async ({ input }) => { .query(async ({ input }) => {
@@ -250,7 +254,16 @@ export const backupRouter = createTRPCRouter({
const searchPath = baseDir ? `${bucketPath}/${baseDir}` : bucketPath; const searchPath = baseDir ? `${bucketPath}/${baseDir}` : bucketPath;
const listCommand = `rclone lsf ${rcloneFlags.join(" ")} "${searchPath}" | head -n 100`; const listCommand = `rclone lsf ${rcloneFlags.join(" ")} "${searchPath}" | head -n 100`;
const { stdout } = await execAsync(listCommand); let stdout = "";
if (input.serverId) {
const result = await execAsyncRemote(listCommand, input.serverId);
stdout = result.stdout;
} else {
const result = await execAsync(listCommand);
stdout = result.stdout;
}
const files = stdout.split("\n").filter(Boolean); const files = stdout.split("\n").filter(Boolean);
const results = baseDir const results = baseDir