Restore named project snapshots

This commit is contained in:
2026-07-25 22:41:24 +02:00
parent 7c670fece3
commit 384a5769ab
25 changed files with 1315 additions and 206 deletions
@@ -1,7 +1,12 @@
import type { NextFunction, Request, Response } from "express";
import { ProjectRevisionConflictError } from "../../domain/errors/project-revision-conflict.error.js";
import { ProjectSnapshotNameConflictError } from "../../domain/errors/project-snapshot-name-conflict.error.js";
import { createNamedProjectSnapshotSchema } from "../../shared/validation/project-snapshot.schemas.js";
import { ProjectStateConflictError } from "../../domain/errors/project-state-conflict.error.js";
import {
createNamedProjectSnapshotSchema,
restoreProjectSnapshotSchema,
} from "../../shared/validation/project-snapshot.schemas.js";
import { projectCommandService } from "../composition/project-command-stores.js";
import { projectSnapshotStore } from "../composition/project-snapshot-store.js";
export function listProjectSnapshots(req: Request, res: Response) {
@@ -59,6 +64,62 @@ export function createNamedProjectSnapshot(
}
}
export function restoreProjectSnapshot(
req: Request,
res: Response,
next: NextFunction
) {
const projectId = getProjectId(req, res);
if (!projectId) {
return;
}
const { snapshotId } = req.params;
if (typeof snapshotId !== "string" || !snapshotId.trim()) {
return res.status(400).json({ error: "Invalid snapshotId" });
}
const parsed = restoreProjectSnapshotSchema.safeParse(req.body);
if (!parsed.success) {
return res.status(400).json({ error: parsed.error.flatten() });
}
try {
const prepared = projectSnapshotStore.prepareRestore(
projectId,
snapshotId
);
if (!prepared) {
return res.status(404).json({ error: "Project snapshot not found" });
}
const result = projectCommandService.executeRestore({
projectId,
expectedRevision: parsed.data.expectedRevision,
description: `Snapshot "${prepared.snapshot.name}" wiederherstellen`,
command: prepared.command,
});
return res.json({
snapshot: prepared.snapshot,
revision: result.revision,
history: result.history,
});
} catch (error) {
if (error instanceof ProjectRevisionConflictError) {
return res.status(409).json({
error: error.message,
code: "PROJECT_REVISION_CONFLICT",
expectedRevision: error.expectedRevision,
currentRevision: error.actualRevision,
});
}
if (error instanceof ProjectStateConflictError) {
return res.status(409).json({
error: error.message,
code: "PROJECT_STATE_CONFLICT",
});
}
return next(error);
}
}
function getProjectId(req: Request, res: Response) {
const { projectId } = req.params;
if (typeof projectId !== "string" || !projectId.trim()) {