Directus als Daten-/Auth-Schicht + Backend-Anbindung
Directus (Postgres, hinter NPM) wird die zentrale Datenquelle für Kunden, Aufträge, Produkte, Preise und Gutscheine; das skrift-backend bleibt reine Generierungs-Engine. - Docker/directus: docker-compose (Directus 11 + Postgres), Bootstrap-Skript legt Collections, Rollen/Policies/Rechte und Seed idempotent an. - Extension skrift-auth: passwortloser Login per E-Mail-Code (Codes nur als Hash, Ablauf, Versuchslimit) sowie Kontoanlage aus dem Checkout. - Extension skrift-orders: kontrollierter Bestell-Endpunkt für Gäste und eingeloggte Kunden; Preis wird immer serverseitig berechnet (editierbare Formel via sicherem Interpreter, Werte aus price_items). - skrift-backend: neuer Endpunkt /api/order/from-directus erzeugt SVGs aus einem Directus-Auftrag und schreibt Status + artifact_path zurück. Dafür wurde die Kernlogik aus generateOrder als runGeneration herausgelöst (Verhalten unverändert); generateOrder ist jetzt ein HTTP-Wrapper. - compose: NPM-Netz ergänzt, API_TOKEN/DIRECTUS_* werden durchgereicht. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -138,23 +138,19 @@ async function finalizeOrder(req, res, next) {
|
||||
* POST /api/order/generate
|
||||
* Generate order from scratch without using cache
|
||||
*/
|
||||
async function generateOrder(req, res, next) {
|
||||
try {
|
||||
const { orderNumber, letters } = req.body;
|
||||
|
||||
async function runGeneration({ orderNumber, letters, metadata }) {
|
||||
{
|
||||
// Validation
|
||||
if (!orderNumber) {
|
||||
return res.status(400).json({
|
||||
error: 'Invalid request',
|
||||
message: 'orderNumber is required'
|
||||
});
|
||||
const e = new Error('orderNumber is required');
|
||||
e.status = 400;
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (!letters || !Array.isArray(letters) || letters.length === 0) {
|
||||
return res.status(400).json({
|
||||
error: 'Invalid request',
|
||||
message: 'Letters array is required and must not be empty'
|
||||
});
|
||||
const e = new Error('Letters array is required and must not be empty');
|
||||
e.status = 400;
|
||||
throw e;
|
||||
}
|
||||
|
||||
console.log(`[Order] Generating order: ${orderNumber}`);
|
||||
@@ -255,10 +251,10 @@ async function generateOrder(req, res, next) {
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`[Order] Scriptalization failed for font ${font}:`, err.message);
|
||||
return res.status(500).json({
|
||||
error: 'Scriptalization failed',
|
||||
message: `Failed to scriptalize texts with 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,10 +286,10 @@ async function generateOrder(req, res, next) {
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`[Order] SVG generation failed for letter ${i}:`, err.message);
|
||||
return res.status(500).json({
|
||||
error: 'SVG generation failed',
|
||||
message: `Failed to generate SVG 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;
|
||||
}
|
||||
|
||||
// Save SVG file - unterscheide zwischen Briefen und Umschlägen
|
||||
@@ -352,7 +348,7 @@ async function generateOrder(req, res, next) {
|
||||
}
|
||||
|
||||
// Step 6: Create order metadata
|
||||
const meta = req.body.metadata || {};
|
||||
const meta = metadata || {};
|
||||
const orderMetadata = {
|
||||
orderNumber,
|
||||
generatedAt: new Date().toISOString(),
|
||||
@@ -373,8 +369,7 @@ async function generateOrder(req, res, next) {
|
||||
|
||||
console.log(`[Order] Order ${orderNumber} generated successfully`);
|
||||
|
||||
// Response
|
||||
res.status(200).json({
|
||||
return {
|
||||
orderNumber,
|
||||
outputPath: outputDir,
|
||||
letters: generatedLetters,
|
||||
@@ -382,9 +377,26 @@ async function generateOrder(req, res, next) {
|
||||
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);
|
||||
}
|
||||
@@ -535,6 +547,7 @@ async function registerOrder(req, res, next) {
|
||||
module.exports = {
|
||||
finalizeOrder,
|
||||
generateOrder,
|
||||
runGeneration,
|
||||
uploadMotif,
|
||||
uploadOrderFile,
|
||||
registerOrder,
|
||||
|
||||
Reference in New Issue
Block a user