feat(frontend): restore whole snapshot

This commit is contained in:
Nicolas Meienberger
2025-10-31 21:52:54 +01:00
parent c7db88fb56
commit ee79fce2aa
8 changed files with 203 additions and 24 deletions

View File

@@ -12,7 +12,6 @@ import {
listSnapshotsFilters,
restoreSnapshotBody,
restoreSnapshotDto,
type CreateRepositoryDto,
type DeleteRepositoryDto,
type GetRepositoryDto,
type ListRepositoriesDto,
@@ -93,9 +92,9 @@ export const repositoriesController = new Hono()
)
.post("/:name/restore", restoreSnapshotDto, validator("json", restoreSnapshotBody), async (c) => {
const { name } = c.req.param();
const { snapshotId, targetPath, path, include, exclude } = c.req.valid("json");
const { snapshotId, path, include, exclude } = c.req.valid("json");
const result = await repositoriesService.restoreSnapshot(name, snapshotId, targetPath, { path, include, exclude });
const result = await repositoriesService.restoreSnapshot(name, snapshotId, { path, include, exclude });
return c.json<RestoreSnapshotDto>(result, 200);
});

View File

@@ -219,7 +219,6 @@ export const listSnapshotFilesDto = describeRoute({
*/
export const restoreSnapshotBody = type({
snapshotId: "string",
targetPath: "string",
path: "string?",
include: "string[]?",
exclude: "string[]?",
@@ -231,8 +230,7 @@ export const restoreSnapshotResponse = type({
success: "boolean",
message: "string",
filesRestored: "number",
filesUpdated: "number",
totalBytes: "number",
filesSkipped: "number",
});
export type RestoreSnapshotDto = typeof restoreSnapshotResponse.infer;

View File

@@ -164,7 +164,6 @@ const listSnapshotFiles = async (name: string, snapshotId: string, path?: string
const restoreSnapshot = async (
name: string,
snapshotId: string,
targetPath: string,
options?: {
path?: string;
include?: string[];
@@ -179,14 +178,13 @@ const restoreSnapshot = async (
throw new NotFoundError("Repository not found");
}
const result = await restic.restore(repository.config, snapshotId, targetPath, options);
const result = await restic.restore(repository.config, snapshotId, "/", options);
return {
success: true,
message: "Snapshot restored successfully",
filesRestored: result.files_restored,
filesUpdated: result.files_updated,
totalBytes: result.total_bytes,
filesSkipped: result.files_skipped,
};
};

View File

@@ -161,11 +161,10 @@ const backup = async (
const restoreOutputSchema = type({
message_type: "'summary'",
total_files: "number",
files_restored: "number",
files_updated: "number",
files_unchanged: "number",
total_bytes: "number",
total_errors: "number?",
files_skipped: "number",
bytes_skipped: "number",
});
const restore = async (
@@ -216,14 +215,15 @@ const restore = async (
logger.info(`Restic restore completed for snapshot ${snapshotId} to target ${target}`);
return {
message_type: "summary" as const,
total_files: 0,
files_restored: 0,
files_updated: 0,
files_unchanged: 0,
total_bytes: 0,
files_skipped: 0,
bytes_skipped: 0,
};
}
const resSummary = JSON.parse(lastLine);
const result = restoreOutputSchema(resSummary);
if (result instanceof type.errors) {
@@ -231,15 +231,15 @@ const restore = async (
logger.info(`Restic restore completed for snapshot ${snapshotId} to target ${target}`);
return {
message_type: "summary" as const,
total_files: 0,
files_restored: 0,
files_updated: 0,
files_unchanged: 0,
total_bytes: 0,
files_skipped: 0,
bytes_skipped: 0,
};
}
logger.info(
`Restic restore completed for snapshot ${snapshotId} to target ${target}: ${result.files_restored} restored, ${result.files_updated} updated`,
`Restic restore completed for snapshot ${snapshotId} to target ${target}: ${result.files_restored} restored, ${result.files_skipped} skipped`,
);
return result;