Add named project snapshots

This commit is contained in:
2026-07-25 22:15:22 +02:00
parent 5a0c9019af
commit 7c670fece3
21 changed files with 3076 additions and 7 deletions
@@ -0,0 +1,69 @@
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 { projectSnapshotStore } from "../composition/project-snapshot-store.js";
export function listProjectSnapshots(req: Request, res: Response) {
const projectId = getProjectId(req, res);
if (!projectId) {
return;
}
const snapshots = projectSnapshotStore.listByProject(projectId);
if (!snapshots) {
return res.status(404).json({ error: "Project not found" });
}
return res.json(snapshots);
}
export function createNamedProjectSnapshot(
req: Request,
res: Response,
next: NextFunction
) {
const projectId = getProjectId(req, res);
if (!projectId) {
return;
}
const parsed = createNamedProjectSnapshotSchema.safeParse(req.body);
if (!parsed.success) {
return res.status(400).json({ error: parsed.error.flatten() });
}
try {
const snapshot = projectSnapshotStore.createNamed({
projectId,
...parsed.data,
});
if (!snapshot) {
return res.status(404).json({ error: "Project not found" });
}
return res.status(201).json(snapshot);
} 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 ProjectSnapshotNameConflictError) {
return res.status(409).json({
error: error.message,
code: "PROJECT_SNAPSHOT_NAME_CONFLICT",
snapshotName: error.snapshotName,
});
}
return next(error);
}
}
function getProjectId(req: Request, res: Response) {
const { projectId } = req.params;
if (typeof projectId !== "string" || !projectId.trim()) {
res.status(400).json({ error: "Invalid projectId" });
return null;
}
return projectId;
}