/** * Directus-Client für das Backend. * * Directus ist die Datenquelle für Aufträge; dieses Backend bleibt die reine * Generierungs-Engine. Zugriff über einen Service-Token (Rolle "Service"). */ const config = require('../config'); function requireConfig() { if (!config.directus.url || !config.directus.token) { const e = new Error('DIRECTUS_URL und DIRECTUS_TOKEN müssen gesetzt sein.'); e.status = 500; throw e; } } async function directusFetch(path, options = {}) { requireConfig(); const res = await fetch(config.directus.url.replace(/\/$/, '') + path, { ...options, headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${config.directus.token}`, ...(options.headers || {}), }, }); const text = await res.text(); let data = null; try { data = text ? JSON.parse(text) : null; } catch { /* ignore */ } if (!res.ok) { const msg = data?.errors?.[0]?.message || text || res.statusText; const e = new Error(`Directus ${options.method || 'GET'} ${path}: ${msg}`); e.status = res.status === 404 ? 404 : 502; throw e; } return data?.data ?? data; } /** Lädt einen Auftrag inkl. Empfängerzeilen, Produkt und Format. */ async function fetchOrder(orderId) { const fields = [ 'id', 'order_number', 'customer_email', 'font', 'status', 'needs_envelope', 'envelope_labeling', 'envelope_text', 'envelope_format', 'text_template', 'motif_mode', 'own_cards', 'entries_count', 'preview_session', 'source_file', 'product.key', 'product.type', 'product.input_mode', 'format.key', 'font_scale_letter', 'font_scale_envelope', 'signatures_manual', 'multi_page', 'realistic', ].join(','); const order = await directusFetch(`/items/orders/${encodeURIComponent(orderId)}?fields=${fields}`); if (!order) { const e = new Error(`Auftrag ${orderId} nicht gefunden.`); e.status = 404; throw e; } const entries = await directusFetch( `/items/order_entries?filter[order][_eq]=${encodeURIComponent(orderId)}` + `&sort=letter_number&limit=-1&fields=*` ); return { order, entries: entries || [] }; } /** Schreibt Status und Artefakt-Pfad zurück. */ async function updateOrder(orderId, patch) { return directusFetch(`/items/orders/${encodeURIComponent(orderId)}`, { method: 'PATCH', body: JSON.stringify(patch), }); } /** Protokolliert einen Statuswechsel in der Timeline. */ async function addStatusHistory(orderId, status, note) { return directusFetch('/items/status_history', { method: 'POST', body: JSON.stringify({ order: orderId, status, note }), }); } /** Lädt mehrere Aufträge (nur die für den Charge-Sammeldruck nötigen Felder), * sortiert nach Auftragsnummer. */ async function fetchOrdersMinimal(ids) { const idList = ids.map((v) => encodeURIComponent(v)).join(','); return directusFetch( `/items/orders?filter[id][_in]=${idList}&limit=-1&sort=order_number` + '&fields=id,order_number,source_files,source_file', ); } /** Lädt eine Directus-Datei (z. B. hochgeladenes Brief-PDF) als Buffer. */ async function fetchAssetBuffer(fileId) { requireConfig(); const res = await fetch(config.directus.url.replace(/\/$/, '') + '/assets/' + encodeURIComponent(fileId), { headers: { Authorization: `Bearer ${config.directus.token}` }, }); if (!res.ok) { const e = new Error(`Directus asset ${fileId}: ${res.status}`); e.status = res.status === 404 ? 404 : 502; throw e; } return Buffer.from(await res.arrayBuffer()); } /** Zentrale Format-Definitionen (Seitengröße, Ränder, Zeilenlimit) aus Directus. */ async function fetchRenderFormats() { return directusFetch( '/items/render_formats?filter[active][_eq]=true&limit=-1' + '&fields=key,page_width_mm,page_height_mm,margin_top_mm,margin_left_mm,margin_right_mm,line_height_factor,line_limit', ); } module.exports = { fetchOrder, updateOrder, addStatusHistory, fetchRenderFormats, fetchOrdersMinimal, fetchAssetBuffer };