feat(repositories): list snapshots api

This commit is contained in:
Nicolas Meienberger
2025-10-23 20:22:09 +02:00
parent 4ae738ce41
commit cae8538b2e
9 changed files with 187 additions and 5 deletions

View File

@@ -8,6 +8,8 @@ import {
type GetRepositoryResponseDto,
type ListRepositoriesResponseDto,
listRepositoriesDto,
listSnapshotsDto,
type ListSnapshotsResponseDto,
} from "./repositories.dto";
import { repositoriesService } from "./repositories.service";
@@ -52,4 +54,26 @@ export const repositoriesController = new Hono()
await repositoriesService.deleteRepository(name);
return c.json({ message: "Repository deleted" }, 200);
})
.get("/:name/snapshots", listSnapshotsDto, async (c) => {
const { name } = c.req.param();
const res = await repositoriesService.listSnapshots(name);
const snapshots = res.map((snapshot) => {
const { summary } = snapshot;
const { backup_start, backup_end } = summary;
const duration = new Date(backup_end).getTime() - new Date(backup_start).getTime();
return {
short_id: snapshot.short_id,
duration,
paths: snapshot.paths,
size: summary.total_bytes_processed,
time: new Date(snapshot.time).getTime(),
};
});
const response = { snapshots } satisfies ListSnapshotsResponseDto;
return c.json(response, 200);
});

View File

@@ -127,3 +127,36 @@ export const deleteRepositoryDto = describeRoute({
},
},
});
/**
* List snapshots in a repository
*/
export const snapshotSchema = type({
short_id: "string",
time: "number",
paths: "string[]",
size: "number",
duration: "number",
});
const listSnapshotsResponse = type({
snapshots: snapshotSchema.array(),
});
export type ListSnapshotsResponseDto = typeof listSnapshotsResponse.infer;
export const listSnapshotsDto = describeRoute({
description: "List all snapshots in a repository",
tags: ["Repositories"],
operationId: "listSnapshots",
responses: {
200: {
description: "List of snapshots",
content: {
"application/json": {
schema: resolver(listSnapshotsResponse),
},
},
},
},
});

View File

@@ -105,9 +105,23 @@ const deleteRepository = async (name: string) => {
await db.delete(repositoriesTable).where(eq(repositoriesTable.name, name));
};
const listSnapshots = async (name: string) => {
const repository = await db.query.repositoriesTable.findFirst({
where: eq(repositoriesTable.name, name),
});
if (!repository) {
throw new NotFoundError("Repository not found");
}
const snapshots = await restic.snapshots(repository.config);
return snapshots;
};
export const repositoriesService = {
listRepositories,
createRepository,
getRepository,
deleteRepository,
listSnapshots,
};