Co-authored-by: Cursor <[email protected]>
This commit is contained in:
lejay
2026-05-28 07:20:38 +02:00
co-authored by Cursor
parent 6923c319a1
commit 6c0d7e47b8
81 changed files with 21780 additions and 2986 deletions
+233 -5
View File
@@ -4,7 +4,7 @@ import path from 'path';
import fs from 'fs';
import { fileURLToPath } from 'url';
import cors from 'cors';
import { dbReady } from './db/index.js';
import { dbReady, run, query } from './db/index.js';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
dotenv.config({ path: path.join(__dirname, '.env') });
@@ -12,12 +12,32 @@ import { getProducts } from './routes/products.js';
import { getBlockTypes } from './routes/blockTypes.js';
import { getFinishes } from './routes/finishes.js';
import { getServiceOptions } from './routes/serviceOptions.js';
import { getSettings } from './routes/settings.js';
import { getSettings, updateSettings } from './routes/settings.js';
import {
ensureEngineeringRulesSeed,
getEngineeringRules,
createEngineeringRule,
updateEngineeringRule,
deleteEngineeringRule
} from './routes/engineeringRules.js';
import { postPrice } from './routes/price.js';
import { getOrders, postOrder } from './routes/orders.js';
import {
getOrders,
postOrder,
getOrderSupplierVouchers,
listOrderPlanExports,
createOrderPlanExport,
downloadOrderPlanExport,
generateOrderPlanExports,
getOrderProductionDetails,
deleteOrderPlanExport
} from './routes/orders.js';
import { getSuppliers, createSupplier, updateSupplier, deleteSupplier } from './routes/suppliers.js';
import {
triggerFormexSync,
streamFormexSync,
triggerHaefeleSync,
streamHaefeleSync,
getLogs,
getStatus,
getFormexMaterials,
@@ -26,7 +46,9 @@ import {
updateFormexMaterial,
deleteFormexMaterial,
getFormexCollections,
getFormexCategories
getFormexCategories,
getFormexSuppliers,
getHaefeleMaterials
} from './routes/sync.js';
import {
uploadMiddleware,
@@ -53,7 +75,11 @@ import {
generatePlansRoute,
generateCuttingListRoute,
exportCuttingListText,
exportPDFHtml
exportPDFHtml,
exportCncRoute,
exportMprRoute,
exportMprPieceRoute,
generateHardwareListRoute
} from './routes/plans.js';
import {
designTipsRoute,
@@ -76,13 +102,33 @@ app.get('/api/block-types', getBlockTypes);
app.get('/api/finishes', getFinishes);
app.get('/api/service-options', getServiceOptions);
app.get('/api/settings', getSettings);
app.put('/api/settings', updateSettings);
app.get('/api/engineering-rules', getEngineeringRules);
app.post('/api/engineering-rules', createEngineeringRule);
app.put('/api/engineering-rules/:id', updateEngineeringRule);
app.delete('/api/engineering-rules/:id', deleteEngineeringRule);
app.post('/api/price', postPrice);
app.get('/api/orders', getOrders);
app.post('/api/orders', postOrder);
app.get('/api/orders/:id/supplier-vouchers', getOrderSupplierVouchers);
app.get('/api/orders/:id/plan-exports', listOrderPlanExports);
app.post('/api/orders/:id/plan-exports', createOrderPlanExport);
app.post('/api/orders/:id/plan-exports/generate', generateOrderPlanExports);
app.get('/api/orders/:id/plan-exports/:exportId/download', downloadOrderPlanExport);
app.delete('/api/orders/:id/plan-exports/:exportId', deleteOrderPlanExport);
app.get('/api/orders/:id/production-details', getOrderProductionDetails);
// Supplier routes
app.get('/api/suppliers', getSuppliers);
app.post('/api/suppliers', createSupplier);
app.put('/api/suppliers/:id', updateSupplier);
app.delete('/api/suppliers/:id', deleteSupplier);
// Sync routes (Formex crawler)
app.post('/api/sync/formex', triggerFormexSync);
app.get('/api/sync/formex/stream', streamFormexSync);
app.post('/api/sync/haefele', triggerHaefeleSync);
app.get('/api/sync/haefele/stream', streamHaefeleSync);
app.get('/api/sync/logs', getLogs);
app.get('/api/sync/status', getStatus);
@@ -94,6 +140,8 @@ app.put('/api/formex/materials/:id', updateFormexMaterial);
app.delete('/api/formex/materials/:id', deleteFormexMaterial);
app.get('/api/formex/collections', getFormexCollections);
app.get('/api/formex/categories', getFormexCategories);
app.get('/api/formex/suppliers', getFormexSuppliers);
app.get('/api/haefele/materials', getHaefeleMaterials);
// Room analysis routes (AI-powered)
app.post('/api/room/analyze', uploadMiddleware, analyzeRoom);
@@ -125,8 +173,12 @@ app.post('/api/ai/render', renderRoute);
// Plans and cutting list routes
app.post('/api/plans/generate', generatePlansRoute);
app.post('/api/plans/cutting-list', generateCuttingListRoute);
app.post('/api/plans/hardware-list', generateHardwareListRoute);
app.post('/api/plans/export-text', exportCuttingListText);
app.post('/api/plans/export-pdf', exportPDFHtml);
app.post('/api/plans/export-cnc', exportCncRoute);
app.post('/api/plans/export-mpr', exportMprRoute);
app.post('/api/plans/export-mpr-piece', exportMprPieceRoute);
if (hasClientBuild) {
app.use(express.static(clientDist));
@@ -152,6 +204,182 @@ if (hasClientBuild) {
await dbReady;
// Apply migration 007: suppliers table + supplier_id columns
async function applyMigration007() {
try {
await run(`CREATE TABLE IF NOT EXISTS suppliers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
email TEXT NOT NULL,
type TEXT NOT NULL,
active INTEGER DEFAULT 1,
created_at TEXT DEFAULT (datetime('now'))
)`);
// Add supplier_id to formex_materials if missing
const fmCols = (await query("PRAGMA table_info(formex_materials)")).rows;
if (!fmCols.find(c => c.name === 'supplier_id')) {
await run('ALTER TABLE formex_materials ADD COLUMN supplier_id INTEGER REFERENCES suppliers(id)');
}
// Add supplier_id to blum_hardware if missing
const bhCols = (await query("PRAGMA table_info(blum_hardware)")).rows;
if (!bhCols.find(c => c.name === 'supplier_id')) {
await run('ALTER TABLE blum_hardware ADD COLUMN supplier_id INTEGER REFERENCES suppliers(id)');
}
// Add supplier_id to handle_variants if missing
const hvCols = (await query("PRAGMA table_info(handle_variants)")).rows;
if (!hvCols.find(c => c.name === 'supplier_id')) {
await run('ALTER TABLE handle_variants ADD COLUMN supplier_id INTEGER REFERENCES suppliers(id)');
}
// Add supplier_id to hinge_variants if missing
const hingeCols = (await query("PRAGMA table_info(hinge_variants)")).rows;
if (!hingeCols.find(c => c.name === 'supplier_id')) {
await run('ALTER TABLE hinge_variants ADD COLUMN supplier_id INTEGER REFERENCES suppliers(id)');
}
// Seed default suppliers
await run(`INSERT OR IGNORE INTO suppliers (code, name, email, type) VALUES
('formex', 'Formex', '[email protected]', 'panels'),
('blum', 'Blum Switzerland', '[email protected]', 'hardware'),
('haefele', 'Hafele', '[email protected]', 'materials')`);
// Assign existing rows to their suppliers
await run(`UPDATE formex_materials SET supplier_id = (SELECT id FROM suppliers WHERE code = 'formex') WHERE supplier_id IS NULL`);
await run(`UPDATE blum_hardware SET supplier_id = (SELECT id FROM suppliers WHERE code = 'blum') WHERE supplier_id IS NULL`);
await run(`UPDATE handle_variants SET supplier_id = (SELECT id FROM suppliers WHERE code = 'blum') WHERE supplier_id IS NULL`);
await run(`UPDATE hinge_variants SET supplier_id = (SELECT id FROM suppliers WHERE code = 'blum') WHERE supplier_id IS NULL`);
console.log('[migration-007] Suppliers table ready.');
} catch (err) {
console.error('[migration-007] Error:', err.message);
}
}
await applyMigration007();
// Apply migration 008: price_unit column on formex_materials
async function applyMigration008() {
try {
const cols = (await query("PRAGMA table_info(formex_materials)")).rows;
if (!cols.find(c => c.name === 'price_unit')) {
await run('ALTER TABLE formex_materials ADD COLUMN price_unit TEXT');
console.log('[migration-008] Added price_unit column to formex_materials.');
}
} catch (err) {
console.error('[migration-008] Error:', err.message);
}
}
await applyMigration008();
// Apply migration 009: persist generated plan exports
async function applyMigration009() {
try {
await run(`CREATE TABLE IF NOT EXISTS plan_exports (
id INTEGER PRIMARY KEY AUTOINCREMENT,
order_id INTEGER NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
export_type TEXT NOT NULL,
file_name TEXT NOT NULL,
file_path TEXT NOT NULL,
mime_type TEXT,
created_at TEXT DEFAULT (datetime('now'))
)`);
await run('CREATE INDEX IF NOT EXISTS idx_plan_exports_order ON plan_exports(order_id)');
console.log('[migration-009] Plan exports table ready.');
} catch (err) {
console.error('[migration-009] Error:', err.message);
}
}
await applyMigration009();
async function applyMigration010() {
try {
const cols = (await query("PRAGMA table_info(formex_materials)")).rows;
if (!cols.find(c => c.name === 'bore_diameter_mm')) {
await run('ALTER TABLE formex_materials ADD COLUMN bore_diameter_mm REAL');
console.log('[migration-010] Added bore_diameter_mm to formex_materials.');
}
} catch (err) {
console.error('[migration-010] Error:', err.message);
}
}
await applyMigration010();
async function applyMigration011() {
try {
const cols = (await query("PRAGMA table_info(formex_materials)")).rows;
const colNames = cols.map(c => c.name);
if (!colNames.includes('bore_depth_base_mm')) {
await run('ALTER TABLE formex_materials ADD COLUMN bore_depth_base_mm REAL');
console.log('[migration-011] Added bore_depth_base_mm to formex_materials.');
}
if (!colNames.includes('bore_depth_piece_mm')) {
await run('ALTER TABLE formex_materials ADD COLUMN bore_depth_piece_mm REAL');
console.log('[migration-011] Added bore_depth_piece_mm to formex_materials.');
}
} catch (err) {
console.error('[migration-011] Error:', err.message);
}
}
await applyMigration011();
async function applyMigration012() {
try {
const cols = (await query("PRAGMA table_info(formex_materials)")).rows;
const colNames = cols.map(c => c.name);
if (!colNames.includes('diameter_mm')) {
await run('ALTER TABLE formex_materials ADD COLUMN diameter_mm REAL');
console.log('[migration-012] Added diameter_mm to formex_materials.');
}
if (!colNames.includes('length_mm')) {
await run('ALTER TABLE formex_materials ADD COLUMN length_mm REAL');
console.log('[migration-012] Added length_mm to formex_materials.');
}
} catch (err) {
console.error('[migration-012] Error:', err.message);
}
}
await applyMigration012();
async function applyMigration013() {
try {
await run(`CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value TEXT
)`);
const defaults = [
['allow_sliding_doors', '0'],
['sanitary_gap_mm', '30'],
['drawer_depth_min_mm', '400'],
['drawer_depth_max_mm', '650'],
['drawer_depth_step_mm', '50'],
['drawer_back_gap_mm', '10'],
['drawer_hidden_handle_reduction_mm', '40'],
['standard_handle_axis_mm', '1000'],
['standard_handle_tolerance_mm', '200'],
['groove_width_mm', '9'],
['groove_depth_mm', '10.5'],
['groove_edge_offset_mm', '16']
];
for (const [key, value] of defaults) {
await run('INSERT OR IGNORE INTO settings (key, value) VALUES ($1, $2)', [key, value]);
}
console.log('[migration-013] Rule settings defaults ready.');
} catch (err) {
console.error('[migration-013] Error:', err.message);
}
}
await applyMigration013();
await ensureEngineeringRulesSeed();
// Initialize the scheduler for automated sync
initScheduler();