forked from jappel/leistungsbilanz-ts
Add Revit CSV preview API
This commit is contained in:
parent
219da5ec3b
commit
caa6caf99f
14 changed files with 437 additions and 3 deletions
117
src/server/controllers/external-csv.controller.ts
Normal file
117
src/server/controllers/external-csv.controller.ts
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
import crypto from "node:crypto";
|
||||
import type { Request, Response } from "express";
|
||||
import { assertExternalCsvConfiguration } from "../../external-model/csv/external-csv-configuration.js";
|
||||
import { ExternalCsvParseError } from "../../external-model/csv/external-csv-transport.js";
|
||||
import { createExternalCsvPreview } from "../../external-model/application/external-csv-preview.js";
|
||||
import { createExternalCsvConfigurationUpdateProjectCommand } from "../../domain/models/external-csv-configuration-project-command.model.js";
|
||||
import {
|
||||
previewExternalCsvSchema,
|
||||
updateExternalCsvConfigurationSchema,
|
||||
} from "../../shared/validation/external-csv.schemas.js";
|
||||
import { externalCsvConfigurationRepository } from "../composition/application-repositories.js";
|
||||
import { projectCommandService } from "../composition/project-command-stores.js";
|
||||
import { respondWithProjectCommandError } from "./project-command.controller.js";
|
||||
|
||||
export function getExternalCsvConfiguration(req: Request, res: Response) {
|
||||
const projectId = getProjectId(req, res);
|
||||
if (!projectId) return;
|
||||
const result = externalCsvConfigurationRepository.getByProject(projectId);
|
||||
if (!result.projectExists) {
|
||||
return res.status(404).json({ error: "Project not found" });
|
||||
}
|
||||
return res.json({ configuration: result.configuration });
|
||||
}
|
||||
|
||||
export function updateExternalCsvConfiguration(req: Request, res: Response) {
|
||||
const projectId = getProjectId(req, res);
|
||||
if (!projectId) return;
|
||||
const parsed = updateExternalCsvConfigurationSchema.safeParse(req.body);
|
||||
if (!parsed.success) {
|
||||
return res.status(400).json({ error: parsed.error.flatten() });
|
||||
}
|
||||
try {
|
||||
assertExternalCsvConfiguration(parsed.data.configuration);
|
||||
const current = externalCsvConfigurationRepository.getByProject(projectId);
|
||||
if (!current.projectExists) {
|
||||
return res.status(404).json({ error: "Project not found" });
|
||||
}
|
||||
const target = {
|
||||
id: current.configuration?.id ?? crypto.randomUUID(),
|
||||
projectId,
|
||||
configurationVersion:
|
||||
(current.configuration?.configurationVersion ?? 0) + 1,
|
||||
configuration: parsed.data.configuration,
|
||||
};
|
||||
const result = projectCommandService.executeUser({
|
||||
projectId,
|
||||
expectedRevision: parsed.data.expectedRevision,
|
||||
description: "Revit-CSV-Konfiguration bearbeiten",
|
||||
command: createExternalCsvConfigurationUpdateProjectCommand(
|
||||
current.configuration,
|
||||
target
|
||||
),
|
||||
});
|
||||
return res.json({ ...result, configuration: target });
|
||||
} catch (error) {
|
||||
return respondWithProjectCommandError(error, res);
|
||||
}
|
||||
}
|
||||
|
||||
export function previewExternalCsv(req: Request, res: Response) {
|
||||
const projectId = getProjectId(req, res);
|
||||
if (!projectId) return;
|
||||
const parsed = previewExternalCsvSchema.safeParse(req.body);
|
||||
if (!parsed.success) {
|
||||
return res.status(400).json({ error: parsed.error.flatten() });
|
||||
}
|
||||
const stored = externalCsvConfigurationRepository.getByProject(projectId);
|
||||
if (!stored.projectExists) {
|
||||
return res.status(404).json({ error: "Project not found" });
|
||||
}
|
||||
if (!stored.configuration) {
|
||||
return res.status(409).json({
|
||||
error: "Für das Projekt ist noch keine Revit-CSV-Konfiguration gespeichert.",
|
||||
code: "EXTERNAL_CSV_CONFIGURATION_REQUIRED",
|
||||
});
|
||||
}
|
||||
try {
|
||||
const bytes = decodeBase64(parsed.data.contentBase64);
|
||||
return res.json(
|
||||
createExternalCsvPreview({
|
||||
fileName: parsed.data.fileName,
|
||||
bytes,
|
||||
configuration: stored.configuration.configuration,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
if (error instanceof ExternalCsvParseError) {
|
||||
return res.status(400).json({ error: error.message, code: error.code });
|
||||
}
|
||||
return res.status(400).json({
|
||||
error: error instanceof Error ? error.message : "CSV-Vorschau fehlgeschlagen.",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function decodeBase64(value: string) {
|
||||
if (!/^[A-Za-z0-9+/]+={0,2}$/.test(value) || value.length % 4 !== 0) {
|
||||
throw new Error("CSV-Inhalt ist nicht gültig Base64-kodiert.");
|
||||
}
|
||||
const bytes = Buffer.from(value, "base64");
|
||||
if (bytes.toString("base64") !== value) {
|
||||
throw new Error("CSV-Inhalt ist nicht kanonisch Base64-kodiert.");
|
||||
}
|
||||
if (bytes.length > 18 * 1024 * 1024) {
|
||||
throw new Error("CSV-Datei überschreitet die maximale Größe von 18 MB.");
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue