diff --git a/b2b-marketing-assistant/server.cjs b/b2b-marketing-assistant/server.cjs index 947ef19e..7fd68693 100644 --- a/b2b-marketing-assistant/server.cjs +++ b/b2b-marketing-assistant/server.cjs @@ -6,230 +6,105 @@ const fs = require('fs'); const path = require('path'); const app = express(); -// Port 3002, um Konflikte mit dem Market Intelligence Tool (3001) und dem React Dev Server (3000) zu vermeiden const PORT = 3002; // Middleware app.use(cors()); -app.use(bodyParser.json({ limit: '10mb' })); // Erhöhe das Limit für potenziell große Payloads +app.use(bodyParser.json({ limit: '10mb' })); -const PYTHON_EXECUTABLE = 'python3'; // Annahme, dass python3 im PATH des Containers ist -// Im Docker-Container liegen server.cjs und das Python-Skript im selben Verzeichnis (/app) +const PYTHON_EXECUTABLE = 'python3'; const SCRIPT_PATH = path.join(__dirname, 'b2b_marketing_orchestrator.py'); -const dbScript = '/app/market_db_manager.py'; // Absoluter Pfad zum DB Manager im Container - +const dbScript = '/app/market_db_manager.py'; // Helper-Funktion zum Ausführen des Python-Skripts const runPythonScript = (args, res) => { console.log(`[${new Date().toISOString()}] Spawning: ${PYTHON_EXECUTABLE} ${args.join(' ')}`); - const pythonProcess = spawn(PYTHON_EXECUTABLE, args); - let pythonOutput = ''; let pythonError = ''; - pythonProcess.stdout.on('data', (data) => { - pythonOutput += data.toString(); - }); - - pythonProcess.stderr.on('data', (data) => { - pythonError += data.toString(); - }); + pythonProcess.stdout.on('data', (data) => { pythonOutput += data.toString(); }); + pythonProcess.stderr.on('data', (data) => { pythonError += data.toString(); }); pythonProcess.on('close', (code) => { - console.log(`[${new Date().toISOString()}] Python script finished with exit code: ${code}`); - - if (pythonError) { - console.log(`--- STDERR ---`); - console.log(pythonError); - console.log(`----------------`); - } - if (code !== 0) { - console.error('Python script exited with an error.'); - return res.status(500).json({ - error: 'An error occurred in the backend script.', - details: pythonError - }); + console.error(`Python error (Code ${code}): ${pythonError}`); + return res.status(500).json({ error: 'Backend error', details: pythonError }); } - try { - const result = JSON.parse(pythonOutput); - res.json(result); - } catch (parseError) { - console.error('Failed to parse Python output as JSON:', parseError); - res.status(500).json({ - error: 'Invalid JSON response from the backend script.', - rawOutput: pythonOutput, - details: pythonError - }); + res.json(JSON.parse(pythonOutput)); + } catch (e) { + res.status(500).json({ error: 'Invalid JSON', raw: pythonOutput }); } }); - - pythonProcess.on('error', (err) => { - console.error('FATAL: Failed to start the python process itself.', err); - res.status(500).json({ - error: 'Failed to start the backend process.', - details: err.message - }); - }); }; +// --- API ROUTES --- +const router = express.Router(); -// API-Endpunkt, um eine neue Analyse zu starten (Schritt 1) -app.post('/api/start-generation', (req, res) => { - console.log(`[${new Date().toISOString()}] HIT: /api/start-generation`); +router.post('/start-generation', (req, res) => { const { companyUrl, language, regions, focus } = req.body; - - if (!companyUrl || !language) { - return res.status(400).json({ error: 'Missing required parameters: companyUrl and language.' }); - } - - const args = [ - SCRIPT_PATH, - '--mode', 'start_generation', - '--url', companyUrl, - '--language', language - ]; - + const args = [SCRIPT_PATH, '--mode', 'start_generation', '--url', companyUrl, '--language', language]; if (regions) args.push('--regions', regions); if (focus) args.push('--focus', focus); - runPythonScript(args, res); }); - -// API-Endpunkt, um den nächsten Schritt zu generieren -app.post('/api/next-step', (req, res) => { - console.log(`[${new Date().toISOString()}] HIT: /api/next-step`); +router.post('/next-step', (req, res) => { const { analysisData, language, channels, generationStep, focusIndustry } = req.body; - - if (!analysisData || !language || generationStep === undefined) { - return res.status(400).json({ error: 'Missing required parameters: analysisData, language, generationStep.' }); - } - - // Wir schreiben die komplexen Kontext-Daten in eine temporäre Datei, um die Kommandozeile sauber zu halten. const tmpDir = path.join(__dirname, 'tmp'); - if (!fs.existsSync(tmpDir)) { - fs.mkdirSync(tmpDir); - } + if (!fs.existsSync(tmpDir)) fs.mkdirSync(tmpDir); const contextFilePath = path.join(tmpDir, `context_${Date.now()}.json`); - try { fs.writeFileSync(contextFilePath, JSON.stringify(analysisData)); - - const args = [ - SCRIPT_PATH, - '--mode', 'next_step', - '--language', language, - '--context_file', contextFilePath, - '--generation_step', generationStep.toString() - ]; + const args = [SCRIPT_PATH, '--mode', 'next_step', '--language', language, '--context_file', contextFilePath, '--generation_step', generationStep.toString()]; + if (channels && Array.isArray(channels)) args.push('--channels', channels.join(',')); + if (focusIndustry) args.push('--focus_industry', focusIndustry); - if (channels && Array.isArray(channels)) { - args.push('--channels', channels.join(',')); - } - - if (focusIndustry) { - args.push('--focus_industry', focusIndustry); - } - - // Da die runPythonScript-Funktion res behandelt, fügen wir hier die Bereinigung hinzu const originalJson = res.json.bind(res); res.json = (data) => { - if (fs.existsSync(contextFilePath)) { - fs.unlinkSync(contextFilePath); - } + if (fs.existsSync(contextFilePath)) fs.unlinkSync(contextFilePath); originalJson(data); }; - - const originalStatus = res.status.bind(res); - res.status = (code) => { - // Wenn ein Fehler auftritt, rufen wir send auf, um die Bereinigung auszulösen - const originalSend = res.send.bind(res); - res.send = (body) => { - if (fs.existsSync(contextFilePath)) { - fs.unlinkSync(contextFilePath); - } - originalSend(body); - } - return originalStatus(code); - } - - runPythonScript(args, res); - - } catch (error) { - console.error('Failed to write temporary context file:', error); - return res.status(500).json({ error: 'Failed to process request context.', details: error.message }); - } + } catch (e) { res.status(500).json({ error: e.message }); } }); -// --- DATABASE ROUTES --- - -app.get('/api/projects', (req, res) => { - runPythonScript([dbScript, 'list'], res); -}); - -app.get('/api/projects/:id', (req, res) => { - runPythonScript([dbScript, 'load', req.params.id], res); -}); - -app.delete('/api/projects/:id', (req, res) => { - runPythonScript([dbScript, 'delete', req.params.id], res); -}); - -app.post('/api/save-project', (req, res) => { - const projectData = req.body; +router.get('/projects', (req, res) => runPythonScript([dbScript, 'list'], res)); +router.get('/projects/:id', (req, res) => runPythonScript([dbScript, 'load', req.params.id], res)); +router.delete('/projects/:id', (req, res) => runPythonScript([dbScript, 'delete', req.params.id], res)); +router.post('/save-project', (req, res) => { const tmpDir = path.join(__dirname, 'tmp'); if (!fs.existsSync(tmpDir)) fs.mkdirSync(tmpDir); const tempFilePath = path.join(tmpDir, `save_${Date.now()}.json`); - try { - fs.writeFileSync(tempFilePath, JSON.stringify(projectData)); + fs.writeFileSync(tempFilePath, JSON.stringify(req.body)); runPythonScript([dbScript, 'save', tempFilePath], res); - } catch (e) { - res.status(500).json({ error: 'Failed to write project data to disk' }); - } + } catch (e) { res.status(500).json({ error: 'Save failed' }); } }); +// Register API Router +app.use('/api', router); +app.use('/b2b/api', router); // Extra safety for Nginx pathing + // --- SERVE STATIC FRONTEND --- -// Serve static files from the 'dist' directory created by `npm run build` -app.use(express.static(path.join(__dirname, 'dist'))); +const distPath = path.join(__dirname, 'dist'); +app.use('/b2b', express.static(distPath)); +app.use(express.static(distPath)); -// Handle client-side routing: return index.html for all non-API routes app.get('*', (req, res) => { - res.sendFile(path.join(__dirname, 'dist', 'index.html')); + // If it's an API call that missed the router, don't serve index.html + if (req.url.startsWith('/api')) return res.status(404).json({ error: 'Not found' }); + res.sendFile(path.join(distPath, 'index.html')); }); -// Separate function to initialize the database, called after server starts const initializeDatabase = () => { console.log(`[${new Date().toISOString()}] Initializing B2B database...`); - const pythonProcess = spawn(PYTHON_EXECUTABLE, [dbScript, 'init']); - - pythonProcess.stdout.on('data', (data) => { - console.log(`[DB Init STDOUT]: ${data.toString().trim()}`); - }); - - pythonProcess.stderr.on('data', (data) => { - console.error(`[DB Init STDERR]: ${data.toString().trim()}`); - }); - - pythonProcess.on('close', (code) => { - if (code === 0) { - console.log(`[${new Date().toISOString()}] B2B database initialized successfully.`); - } else { - console.error(`[${new Date().toISOString()}] B2B database initialization failed with code: ${code}`); - } - }); - - pythonProcess.on('error', (err) => { - console.error(`[${new Date().toISOString()}] Failed to spawn Python for DB init:`, err); - }); + const proc = spawn(PYTHON_EXECUTABLE, [dbScript, 'init']); + proc.on('close', (code) => console.log(`[${new Date().toISOString()}] DB init finished (Code ${code})`)); }; -// Start des Servers app.listen(PORT, () => { - console.log(`B2B Marketing Assistant API Bridge running on http://localhost:${PORT}`); - initializeDatabase(); // Initialize DB after server is listening -}); \ No newline at end of file + console.log(`B2B Marketing Assistant API Bridge running on port ${PORT}`); + initializeDatabase(); +});