Import aus Monorepo (frischer Start)
All checks were successful
Build & Deploy / deploy (push) Successful in 10s
All checks were successful
Build & Deploy / deploy (push) Successful in 10s
This commit is contained in:
601
src/api/controllers/order-controller.js
Normal file
601
src/api/controllers/order-controller.js
Normal file
@@ -0,0 +1,601 @@
|
||||
const fs = require('fs').promises;
|
||||
const fsSync = require('fs');
|
||||
const path = require('path');
|
||||
const config = require('../../config');
|
||||
const { scriptalizeBatch } = require('../../services/scriptalizer-service');
|
||||
const { generateLetterSVG, generateEnvelopeSVG, generateLetterPages } = require('../../lib/svg-generator');
|
||||
const { replacePlaceholders, generateAllCSVs } = require('../../services/placeholder-service');
|
||||
const { TAB_MARKER } = require('../../lib/svg-font-engine');
|
||||
|
||||
/**
|
||||
* POST /api/order/finalize
|
||||
* Finalize order by copying cached previews to output directory
|
||||
*/
|
||||
async function finalizeOrder(req, res, next) {
|
||||
try {
|
||||
const { sessionId, orderNumber } = req.body;
|
||||
|
||||
// Validation
|
||||
if (!sessionId || !orderNumber) {
|
||||
return res.status(400).json({
|
||||
error: 'Invalid request',
|
||||
message: 'sessionId and orderNumber are required'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`[Order] Finalizing order: ${orderNumber} from session: ${sessionId}`);
|
||||
|
||||
// Check if session cache exists
|
||||
const sessionDir = path.join(config.paths.previews, sessionId);
|
||||
|
||||
try {
|
||||
await fs.access(sessionDir);
|
||||
} catch {
|
||||
return res.status(404).json({
|
||||
error: 'Session not found',
|
||||
message: `Preview cache not found for session: ${sessionId}. Please generate previews first.`
|
||||
});
|
||||
}
|
||||
|
||||
// Check cache expiration
|
||||
const metadataPath = path.join(sessionDir, '.metadata.json');
|
||||
try {
|
||||
const metadataContent = await fs.readFile(metadataPath, 'utf8');
|
||||
const metadata = JSON.parse(metadataContent);
|
||||
|
||||
const expiresAt = new Date(metadata.expiresAt);
|
||||
if (new Date() > expiresAt) {
|
||||
return res.status(410).json({
|
||||
error: 'Cache expired',
|
||||
message: 'Preview cache has expired. Please regenerate previews.',
|
||||
expiredAt: metadata.expiresAt
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(`[Order] Metadata not found for session: ${sessionId}`);
|
||||
}
|
||||
|
||||
// Create output directory and subdirectories
|
||||
const outputDir = path.join(config.paths.output, orderNumber);
|
||||
const envelopesDir = path.join(outputDir, 'umschlaege');
|
||||
await fs.mkdir(outputDir, { recursive: true });
|
||||
await fs.mkdir(envelopesDir, { recursive: true });
|
||||
|
||||
// Copy files from cache to output, separating letters and envelopes
|
||||
const files = await fs.readdir(sessionDir);
|
||||
const copiedLetters = [];
|
||||
const copiedEnvelopes = [];
|
||||
const copiedOther = [];
|
||||
|
||||
for (const file of files) {
|
||||
// Skip metadata file
|
||||
if (file === '.metadata.json') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const sourcePath = path.join(sessionDir, file);
|
||||
|
||||
// Determine destination based on file type
|
||||
if (file.startsWith('envelope_')) {
|
||||
// Umschläge in Unterordner 'umschlaege'
|
||||
const destPath = path.join(envelopesDir, file);
|
||||
await fs.copyFile(sourcePath, destPath);
|
||||
copiedEnvelopes.push(file);
|
||||
} else if (file.startsWith('letter_')) {
|
||||
// Briefe im Hauptordner
|
||||
const destPath = path.join(outputDir, file);
|
||||
await fs.copyFile(sourcePath, destPath);
|
||||
copiedLetters.push(file);
|
||||
} else {
|
||||
// Andere Dateien (CSV, etc.) im Hauptordner
|
||||
const destPath = path.join(outputDir, file);
|
||||
await fs.copyFile(sourcePath, destPath);
|
||||
copiedOther.push(file);
|
||||
}
|
||||
}
|
||||
|
||||
// Create order metadata
|
||||
const meta = req.body.metadata || {};
|
||||
const orderMetadata = {
|
||||
orderNumber,
|
||||
sessionId,
|
||||
finalizedAt: new Date().toISOString(),
|
||||
letterCount: copiedLetters.length,
|
||||
envelopeCount: copiedEnvelopes.length,
|
||||
letters: copiedLetters,
|
||||
envelopes: copiedEnvelopes,
|
||||
other: copiedOther,
|
||||
letterFormat: meta.letterFormat || null,
|
||||
envelopeFormat: meta.envelopeFormat || null,
|
||||
envelopeBeschriftung: meta.envelopeBeschriftung || null,
|
||||
};
|
||||
|
||||
const orderMetadataPath = path.join(outputDir, 'order-metadata.json');
|
||||
await fs.writeFile(orderMetadataPath, JSON.stringify(orderMetadata, null, 2), 'utf8');
|
||||
|
||||
const totalFiles = copiedLetters.length + copiedEnvelopes.length + copiedOther.length;
|
||||
console.log(`[Order] Finalized order ${orderNumber}: ${copiedLetters.length} letters, ${copiedEnvelopes.length} envelopes, ${copiedOther.length} other files`);
|
||||
|
||||
// Response
|
||||
res.status(200).json({
|
||||
orderNumber,
|
||||
outputPath: outputDir,
|
||||
letters: copiedLetters,
|
||||
envelopes: copiedEnvelopes,
|
||||
other: copiedOther,
|
||||
totalFiles,
|
||||
envelopesGenerated: 0,
|
||||
timestamp: orderMetadata.finalizedAt
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
console.error('[Order] Error finalizing order:', err);
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/order/generate
|
||||
* Generate order from scratch without using cache
|
||||
*/
|
||||
async function runGeneration({ orderNumber, letters, metadata }) {
|
||||
{
|
||||
// Validation
|
||||
if (!orderNumber) {
|
||||
const e = new Error('orderNumber is required');
|
||||
e.status = 400;
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (!letters || !Array.isArray(letters) || letters.length === 0) {
|
||||
const e = new Error('Letters array is required and must not be empty');
|
||||
e.status = 400;
|
||||
throw e;
|
||||
}
|
||||
|
||||
console.log(`[Order] Generating order: ${orderNumber}`);
|
||||
console.log(`[Order] Total documents: ${letters.length}`);
|
||||
|
||||
// Debug: Zeige welche Dokument-Typen ankommen
|
||||
const letterCount = letters.filter(l => l.type !== 'envelope').length;
|
||||
const envelopeCount = letters.filter(l => l.type === 'envelope').length;
|
||||
console.log(`[Order] Letters: ${letterCount}, Envelopes: ${envelopeCount}`);
|
||||
console.log(`[Order] Document types:`, letters.map(l => ({ index: l.index, type: l.type, envelopeType: l.envelopeType })));
|
||||
|
||||
// Create output directory and subdirectories
|
||||
const outputDir = path.join(config.paths.output, orderNumber);
|
||||
const envelopesDir = path.join(outputDir, 'umschlaege');
|
||||
await fs.mkdir(outputDir, { recursive: true });
|
||||
await fs.mkdir(envelopesDir, { recursive: true });
|
||||
|
||||
// Step 1: Prepare texts and replace placeholders
|
||||
const processedLetters = letters.map((letter, loopIndex) => {
|
||||
let text = letter.text || '';
|
||||
|
||||
// Signatur-Platzhalter separat extrahieren – sie werden NICHT als Text ersetzt
|
||||
const sigPlaceholders = {};
|
||||
const nonSigPlaceholders = {};
|
||||
if (letter.placeholders && typeof letter.placeholders === 'object') {
|
||||
Object.entries(letter.placeholders).forEach(([k, v]) => {
|
||||
if (/^signatur\d+$/i.test(k)) sigPlaceholders[k.toLowerCase()] = v;
|
||||
else nonSigPlaceholders[k] = v;
|
||||
});
|
||||
text = replacePlaceholders(text, nonSigPlaceholders);
|
||||
}
|
||||
|
||||
return {
|
||||
...letter,
|
||||
index: letter.index !== undefined ? letter.index : loopIndex,
|
||||
loopIndex,
|
||||
processedText: text,
|
||||
// Signaturen aus Platzhaltern UND direkt übergebene (manuelle Aufträge).
|
||||
signatures: { ...(letter.signatures || {}), ...sigPlaceholders },
|
||||
format: letter.format || 'a4',
|
||||
font: letter.font || 'tilda',
|
||||
type: letter.type || 'letter',
|
||||
envelopeType: letter.envelopeType || 'recipient',
|
||||
realisticHandwriting: letter.realisticHandwriting ?? false,
|
||||
};
|
||||
});
|
||||
|
||||
// Step 2: Group letters by font for batch scriptalization
|
||||
const lettersByFont = {};
|
||||
|
||||
processedLetters.forEach(letter => {
|
||||
const font = letter.font;
|
||||
if (!lettersByFont[font]) {
|
||||
lettersByFont[font] = [];
|
||||
}
|
||||
lettersByFont[font].push(letter);
|
||||
});
|
||||
|
||||
// Step 3: Scriptalize texts by font
|
||||
const scriptalizedMap = new Map();
|
||||
|
||||
for (const [font, fontLetters] of Object.entries(lettersByFont)) {
|
||||
// Tabs (\t) und Signatur-Marker ([[signaturN]]) vor Scriptalisierung extrahieren,
|
||||
// danach mit TAB_MARKER bzw. [[signaturN]] restaurieren.
|
||||
const scriptalizerInputs = [];
|
||||
const splitMaps = [];
|
||||
|
||||
for (const letter of fontLetters) {
|
||||
const raw = letter.processedText;
|
||||
const parts = raw.split(/(\t|\[\[signatur\d+\]\]|\[\[seitenumbruch\]\]|[&|])/gi);
|
||||
const segments = [], markers = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
if (i % 2 === 0) segments.push(parts[i]);
|
||||
else markers.push(parts[i]);
|
||||
}
|
||||
// Leere Segmente NICHT an den Scriptalizer senden
|
||||
const startIdx = scriptalizerInputs.length;
|
||||
const hasContent = segments.map(s => s.trim().length > 0);
|
||||
segments.forEach((s, j) => { if (hasContent[j]) scriptalizerInputs.push(s); });
|
||||
splitMaps.push({ loopIndex: letter.loopIndex, startIdx, segments, markers, hasContent, fallback: raw });
|
||||
}
|
||||
|
||||
console.log(`[Order] Scriptalizing ${scriptalizerInputs.length} segments with font: ${font}`);
|
||||
|
||||
try {
|
||||
const scriptalizedAll = await scriptalizeBatch(scriptalizerInputs, font, config.scriptalizer.errFrequency);
|
||||
|
||||
for (const { loopIndex, startIdx, segments, markers, hasContent, fallback } of splitMaps) {
|
||||
const nonEmptyCount = hasContent.filter(Boolean).length;
|
||||
if (!nonEmptyCount && !markers.length) { scriptalizedMap.set(loopIndex, fallback); continue; }
|
||||
let si = startIdx;
|
||||
const segs = segments.map((_s, j) => hasContent[j] ? (scriptalizedAll[si++] || '') : '');
|
||||
let reconstructed = segs[0] || '';
|
||||
for (let i = 0; i < markers.length; i++) {
|
||||
reconstructed += (markers[i] === '\t' ? TAB_MARKER : markers[i]);
|
||||
reconstructed += (segs[i + 1] || '');
|
||||
}
|
||||
scriptalizedMap.set(loopIndex, reconstructed);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`[Order] Scriptalization failed for font ${font}:`, err.message);
|
||||
const e = new Error(`Failed to scriptalize texts with font ${font}: ${err.message}`);
|
||||
e.status = 500;
|
||||
e.label = 'Scriptalization failed';
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4: Generate SVG files - separate letters and envelopes
|
||||
const generatedLetters = [];
|
||||
const generatedEnvelopes = [];
|
||||
// Vorschau-Session (falls vorhanden): bereits gezeigte Dokumente werden 1:1
|
||||
// aus dem Vorschau-Cache übernommen, damit der Kunde exakt das gedruckte
|
||||
// Ergebnis erhält, das er gesehen hat (gleiche Zufalls-Handschrift).
|
||||
const previewSession = (metadata || {}).previewSession;
|
||||
const previewsRoot = config.paths.previews;
|
||||
|
||||
for (let i = 0; i < processedLetters.length; i++) {
|
||||
const letter = processedLetters[i];
|
||||
const absoluteIndex = letter.index !== undefined ? letter.index : i;
|
||||
const isEnv = letter.type === 'envelope';
|
||||
const pad = String(absoluteIndex).padStart(3, '0');
|
||||
|
||||
// Mehrseitiger Brief → N Seiten-Dateien letter_NNN_pM.svg (Seite 1..N).
|
||||
if (!isEnv && letter.multiPage) {
|
||||
const scriptalizedText = scriptalizedMap.get(letter.loopIndex) || letter.processedText;
|
||||
let pages;
|
||||
try {
|
||||
({ pages } = generateLetterPages(scriptalizedText, letter.format, {
|
||||
font: letter.font, realisticHandwriting: letter.realisticHandwriting ?? false, fontScale: letter.fontScale,
|
||||
}));
|
||||
} catch (err) {
|
||||
const e = new Error(`Failed to generate SVG for letter ${i}: ${err.message}`);
|
||||
e.status = 500; e.label = 'SVG generation failed'; throw e;
|
||||
}
|
||||
// Entweder ALLE Seiten aus der Vorschau übernehmen oder ALLE neu rendern –
|
||||
// nicht mischen, sonst unterschiedliche Handschrift-Variation je Seite.
|
||||
let alleInVorschau = false;
|
||||
if (previewSession) {
|
||||
alleInVorschau = true;
|
||||
for (let p = 0; p < pages.length; p++) {
|
||||
try { await fs.access(path.join(previewsRoot, previewSession, `letter_${pad}_p${p + 1}.svg`)); }
|
||||
catch { alleInVorschau = false; break; }
|
||||
}
|
||||
}
|
||||
for (let p = 0; p < pages.length; p++) {
|
||||
const fn = `letter_${pad}_p${p + 1}.svg`;
|
||||
const fp = path.join(outputDir, fn);
|
||||
if (alleInVorschau) await fs.copyFile(path.join(previewsRoot, previewSession, fn), fp);
|
||||
else await fs.writeFile(fp, pages[p], 'utf8');
|
||||
generatedLetters.push(fn);
|
||||
}
|
||||
console.log(`[Order] letter_${pad}: ${pages.length} Seiten${alleInVorschau ? ' (aus Vorschau)' : ''}.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const filename = `${isEnv ? 'envelope' : 'letter'}_${pad}.svg`;
|
||||
const filepath = isEnv ? path.join(envelopesDir, filename) : path.join(outputDir, filename);
|
||||
|
||||
// 1:1 aus der Vorschau übernehmen, wenn für dieses Dokument eine
|
||||
// Vorschau-Datei existiert.
|
||||
if (previewSession) {
|
||||
const cached = path.join(previewsRoot, previewSession, filename);
|
||||
try {
|
||||
await fs.access(cached);
|
||||
await fs.copyFile(cached, filepath);
|
||||
(isEnv ? generatedEnvelopes : generatedLetters).push(filename);
|
||||
console.log(`[Order] ${filename} 1:1 aus der Vorschau übernommen.`);
|
||||
continue;
|
||||
} catch { /* keine Vorschau-Datei → normal rendern */ }
|
||||
}
|
||||
|
||||
const scriptalizedText = scriptalizedMap.get(letter.loopIndex) || letter.processedText;
|
||||
let svgResult;
|
||||
try {
|
||||
if (isEnv) {
|
||||
svgResult = generateEnvelopeSVG(
|
||||
scriptalizedText, letter.format, letter.envelopeType,
|
||||
{ font: letter.font, realisticHandwriting: letter.realisticHandwriting ?? false, fontScale: letter.fontScale }
|
||||
);
|
||||
} else {
|
||||
svgResult = generateLetterSVG(
|
||||
scriptalizedText, letter.format,
|
||||
{ font: letter.font, realisticHandwriting: letter.realisticHandwriting ?? false, signatures: letter.signatures || {}, signatureOffsets: letter.signatureOffsets || {}, fontScale: letter.fontScale }
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`[Order] SVG generation failed for letter ${i}:`, err.message);
|
||||
const e = new Error(`Failed to generate SVG for letter ${i}: ${err.message}`);
|
||||
e.status = 500;
|
||||
e.label = 'SVG generation failed';
|
||||
throw e;
|
||||
}
|
||||
|
||||
await fs.writeFile(filepath, svgResult.svg, 'utf8');
|
||||
(isEnv ? generatedEnvelopes : generatedLetters).push(filename);
|
||||
}
|
||||
|
||||
console.log(`[Order] Generated ${generatedLetters.length} letters, ${generatedEnvelopes.length} envelopes`);
|
||||
|
||||
// Step 5: Generate all CSV files if placeholders are present
|
||||
const hasPlaceholders = processedLetters.some(l => l.placeholders && Object.keys(l.placeholders).length > 0);
|
||||
const csvFiles = [];
|
||||
|
||||
if (hasPlaceholders) {
|
||||
try {
|
||||
const csvs = generateAllCSVs(processedLetters);
|
||||
|
||||
// Brief-Platzhalter CSV
|
||||
if (csvs.placeholders) {
|
||||
const filename = 'brief_platzhalter.csv';
|
||||
await fs.writeFile(path.join(outputDir, filename), csvs.placeholders, 'utf8');
|
||||
csvFiles.push(filename);
|
||||
console.log(`[Order] Generated brief_platzhalter.csv`);
|
||||
}
|
||||
|
||||
// Empfänger CSV (für recipientData-Modus)
|
||||
if (csvs.recipients) {
|
||||
const filename = 'empfaenger.csv';
|
||||
await fs.writeFile(path.join(outputDir, filename), csvs.recipients, 'utf8');
|
||||
csvFiles.push(filename);
|
||||
console.log(`[Order] Generated empfaenger.csv`);
|
||||
}
|
||||
|
||||
// Umschlag-Platzhalter CSV (für customText-Modus)
|
||||
if (csvs.envelopePlaceholders) {
|
||||
const filename = 'umschlag_platzhalter.csv';
|
||||
await fs.writeFile(path.join(outputDir, filename), csvs.envelopePlaceholders, 'utf8');
|
||||
csvFiles.push(filename);
|
||||
console.log(`[Order] Generated umschlag_platzhalter.csv`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[Order] CSV generation failed:', err.message);
|
||||
// Don't fail the request, CSV is optional
|
||||
}
|
||||
}
|
||||
|
||||
// Step 6: Create order metadata
|
||||
const meta = metadata || {};
|
||||
const orderMetadata = {
|
||||
orderNumber,
|
||||
generatedAt: new Date().toISOString(),
|
||||
letterCount: generatedLetters.length,
|
||||
envelopeCount: generatedEnvelopes.length,
|
||||
letters: generatedLetters,
|
||||
envelopes: generatedEnvelopes,
|
||||
fonts: Object.keys(lettersByFont),
|
||||
hasPlaceholders,
|
||||
csvFiles,
|
||||
letterFormat: meta.letterFormat || null,
|
||||
envelopeFormat: meta.envelopeFormat || null,
|
||||
envelopeBeschriftung: meta.envelopeBeschriftung || null,
|
||||
};
|
||||
|
||||
const orderMetadataPath = path.join(outputDir, 'order-metadata.json');
|
||||
await fs.writeFile(orderMetadataPath, JSON.stringify(orderMetadata, null, 2), 'utf8');
|
||||
|
||||
// Eingegebenen Text als reine Textdatei mitablegen (falls übergeben).
|
||||
if (meta.rawText) {
|
||||
try { await fs.writeFile(path.join(outputDir, 'brieftext.txt'), String(meta.rawText), 'utf8'); }
|
||||
catch (e) { console.warn('[Order] brieftext.txt nicht geschrieben:', e.message); }
|
||||
}
|
||||
|
||||
console.log(`[Order] Order ${orderNumber} generated successfully`);
|
||||
|
||||
return {
|
||||
orderNumber,
|
||||
outputPath: outputDir,
|
||||
letters: generatedLetters,
|
||||
envelopes: generatedEnvelopes,
|
||||
csvFiles,
|
||||
totalFiles: generatedLetters.length + generatedEnvelopes.length + csvFiles.length,
|
||||
timestamp: orderMetadata.generatedAt
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/order/generate
|
||||
* HTTP-Wrapper um runGeneration – Verhalten unverändert.
|
||||
*/
|
||||
async function generateOrder(req, res, next) {
|
||||
try {
|
||||
const { orderNumber, letters, metadata } = req.body;
|
||||
const result = await runGeneration({ orderNumber, letters, metadata });
|
||||
res.status(200).json(result);
|
||||
} catch (err) {
|
||||
if (err.status) {
|
||||
return res.status(err.status).json({
|
||||
error: err.label || 'Invalid request',
|
||||
message: err.message
|
||||
});
|
||||
}
|
||||
console.error('[Order] Error generating order:', err);
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/order/motif
|
||||
* Upload a motif image for an order
|
||||
*/
|
||||
async function uploadMotif(req, res, next) {
|
||||
try {
|
||||
const { orderNumber } = req.body;
|
||||
|
||||
// Validation
|
||||
if (!orderNumber) {
|
||||
return res.status(400).json({
|
||||
error: 'Invalid request',
|
||||
message: 'orderNumber is required'
|
||||
});
|
||||
}
|
||||
|
||||
if (!req.file) {
|
||||
return res.status(400).json({
|
||||
error: 'Invalid request',
|
||||
message: 'No file uploaded'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`[Order] Uploading motif for order: ${orderNumber}`);
|
||||
console.log(`[Order] File: ${req.file.originalname}, Size: ${req.file.size} bytes, Type: ${req.file.mimetype}`);
|
||||
|
||||
// Create output directory if it doesn't exist
|
||||
const outputDir = path.join(config.paths.output, orderNumber);
|
||||
await fs.mkdir(outputDir, { recursive: true });
|
||||
|
||||
// Determine file extension
|
||||
const originalExt = path.extname(req.file.originalname).toLowerCase() || '.png';
|
||||
const filename = `motif${originalExt}`;
|
||||
const filepath = path.join(outputDir, filename);
|
||||
|
||||
// Write file to disk
|
||||
await fs.writeFile(filepath, req.file.buffer);
|
||||
|
||||
console.log(`[Order] Motif saved to: ${filepath}`);
|
||||
|
||||
// Response
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
orderNumber,
|
||||
filename,
|
||||
path: filepath,
|
||||
size: req.file.size,
|
||||
mimetype: req.file.mimetype
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
console.error('[Order] Error uploading motif:', err);
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/order/files
|
||||
* Speichert eine bereits vom WordPress-Frontend hochgeladene Datei
|
||||
* (Adress-/Platzhalter-Excel, Briefdokument) im Auftragsordner ab,
|
||||
* damit sie zusammen mit den generierten Umschlägen vorliegt.
|
||||
*/
|
||||
async function uploadOrderFile(req, res, next) {
|
||||
try {
|
||||
const { orderNumber, fileType } = req.body;
|
||||
|
||||
if (!orderNumber || !fileType) {
|
||||
return res.status(400).json({
|
||||
error: 'Invalid request',
|
||||
message: 'orderNumber and fileType are required'
|
||||
});
|
||||
}
|
||||
|
||||
if (!req.file) {
|
||||
return res.status(400).json({
|
||||
error: 'Invalid request',
|
||||
message: 'No file uploaded'
|
||||
});
|
||||
}
|
||||
|
||||
const outputDir = path.join(config.paths.output, orderNumber, 'dateien');
|
||||
await fs.mkdir(outputDir, { recursive: true });
|
||||
|
||||
const ext = path.extname(req.file.originalname) || '';
|
||||
const filename = `${fileType}${ext}`;
|
||||
const filepath = path.join(outputDir, filename);
|
||||
|
||||
await fs.writeFile(filepath, req.file.buffer);
|
||||
|
||||
console.log(`[Order] File saved for order ${orderNumber}: ${filename}`);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
orderNumber,
|
||||
fileType,
|
||||
filename,
|
||||
path: filepath,
|
||||
size: req.file.size,
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
console.error('[Order] Error saving order file:', err);
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/order/register
|
||||
* Speichert Metadaten für einen Auftrag ohne SVG-Generierung (z.B. gedruckte Briefe)
|
||||
*/
|
||||
async function registerOrder(req, res, next) {
|
||||
try {
|
||||
const { orderNumber, metadata } = req.body;
|
||||
|
||||
if (!orderNumber) {
|
||||
return res.status(400).json({ error: 'Invalid request', message: 'orderNumber is required' });
|
||||
}
|
||||
|
||||
const outputDir = path.join(config.paths.output, orderNumber);
|
||||
await fs.mkdir(outputDir, { recursive: true });
|
||||
|
||||
const orderMetadata = {
|
||||
orderNumber,
|
||||
registeredAt: new Date().toISOString(),
|
||||
...(metadata || {}),
|
||||
};
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(outputDir, 'order-metadata.json'),
|
||||
JSON.stringify(orderMetadata, null, 2),
|
||||
'utf8'
|
||||
);
|
||||
|
||||
console.log(`[Order] Registered order ${orderNumber}`);
|
||||
res.status(200).json({ orderNumber, registeredAt: orderMetadata.registeredAt });
|
||||
|
||||
} catch (err) {
|
||||
console.error('[Order] Error registering order:', err);
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
finalizeOrder,
|
||||
generateOrder,
|
||||
runGeneration,
|
||||
uploadMotif,
|
||||
uploadOrderFile,
|
||||
registerOrder,
|
||||
};
|
||||
Reference in New Issue
Block a user