const express = require('express'); const { spawn } = require('child_process'); const bodyParser = require('body-parser'); const cors = require('cors'); const fs = require('fs'); const path = require('path'); const app = express(); const PORT = 3001; // Node.js Server läuft auf einem anderen Port als React (3000) // Middleware app.use(cors()); // Ermöglicht Cross-Origin-Requests von der React-App app.use(bodyParser.json()); // Parst JSON-Anfragen // API-Endpunkt für generateSearchStrategy app.post('/api/generate-search-strategy', async (req, res) => { console.log(`[${new Date().toISOString()}] HIT: /api/generate-search-strategy`); const { referenceUrl, contextContent } = req.body; if (!referenceUrl || !contextContent) { console.error('Validation Error: Missing referenceUrl or contextContent.'); return res.status(400).json({ error: 'Missing referenceUrl or contextContent' }); } const tempContextFilePath = path.join(__dirname, 'tmp', `context_${Date.now()}.md`); const tmpDir = path.join(__dirname, 'tmp'); if (!fs.existsSync(tmpDir)) { fs.mkdirSync(tmpDir); } try { fs.writeFileSync(tempContextFilePath, contextContent); console.log(`Successfully wrote context to ${tempContextFilePath}`); const pythonExecutable = path.join(__dirname, '..', '.venv', 'bin', 'python3'); const pythonScript = path.join(__dirname, '..', 'market_intel_orchestrator.py'); const scriptArgs = [pythonScript, '--mode', 'generate_strategy', '--reference_url', referenceUrl, '--context_file', tempContextFilePath]; console.log(`Spawning command: ${pythonExecutable}`); console.log(`With arguments: ${JSON.stringify(scriptArgs)}`); const pythonProcess = spawn(pythonExecutable, scriptArgs, { env: { ...process.env, PYTHONPATH: path.join(__dirname, '..', '.venv', 'lib', 'python3.11', 'site-packages') } }); let pythonOutput = ''; let pythonError = ''; pythonProcess.stdout.on('data', (data) => { pythonOutput += data.toString(); }); pythonProcess.stderr.on('data', (data) => { pythonError += data.toString(); }); pythonProcess.on('close', (code) => { console.log(`Python script finished with exit code: ${code}`); console.log(`--- STDOUT ---`); console.log(pythonOutput); console.log(`--- STDERR ---`); console.log(pythonError); console.log(`----------------`); fs.unlinkSync(tempContextFilePath); if (code !== 0) { console.error(`Python script exited with error.`); return res.status(500).json({ error: 'Python script failed', 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 from Python script', rawOutput: pythonOutput, details: pythonError }); } }); pythonProcess.on('error', (err) => { console.error('FATAL: Failed to start python process itself.', err); if (fs.existsSync(tempContextFilePath)) { fs.unlinkSync(tempContextFilePath); } res.status(500).json({ error: 'Failed to start Python process', details: err.message }); }); } catch (writeError) { console.error('Failed to write temporary context file:', writeError); res.status(500).json({ error: 'Failed to write temporary file', details: writeError.message }); } }); // API-Endpunkt für identifyCompetitors app.post('/api/identify-competitors', async (req, res) => { console.log(`[${new Date().toISOString()}] HIT: /api/identify-competitors`); const { referenceUrl, targetMarket, contextContent, referenceCity, referenceCountry, summaryOfOffer } = req.body; if (!referenceUrl || !targetMarket) { console.error('Validation Error: Missing referenceUrl or targetMarket for identify_competitors.'); return res.status(400).json({ error: 'Missing referenceUrl or targetMarket' }); } const tempContextFilePath = path.join(__dirname, 'tmp', `context_comp_${Date.now()}.md`); const tmpDir = path.join(__dirname, 'tmp'); if (!fs.existsSync(tmpDir)) { fs.mkdirSync(tmpDir); } try { if (contextContent) { fs.writeFileSync(tempContextFilePath, contextContent); console.log(`Successfully wrote context to ${tempContextFilePath} for competitors.`); } const pythonExecutable = path.join(__dirname, '..', '.venv', 'bin', 'python3'); const pythonScript = path.join(__dirname, '..', 'market_intel_orchestrator.py'); const scriptArgs = [ pythonScript, '--mode', 'identify_competitors', '--reference_url', referenceUrl, '--target_market', targetMarket ]; if (contextContent) { scriptArgs.push('--context_file', tempContextFilePath); } if (referenceCity) { scriptArgs.push('--reference_city', referenceCity); } if (referenceCountry) { scriptArgs.push('--reference_country', referenceCountry); } if (summaryOfOffer) { scriptArgs.push('--summary_of_offer', summaryOfOffer); } console.log(`Spawning command: ${pythonExecutable}`); console.log(`With arguments: ${JSON.stringify(scriptArgs)}`); const pythonProcess = spawn(pythonExecutable, scriptArgs, { env: { ...process.env, PYTHONPATH: path.join(__dirname, '..', '.venv', 'lib', 'python3.11', 'site-packages') } }); let pythonOutput = ''; let pythonError = ''; pythonProcess.stdout.on('data', (data) => { pythonOutput += data.toString(); }); pythonProcess.stderr.on('data', (data) => { pythonError += data.toString(); }); pythonProcess.on('close', (code) => { console.log(`Python script (identify_competitors) finished with exit code: ${code}`); console.log(`--- STDOUT (identify_competitors) ---`); console.log(pythonOutput); console.log(`--- STDERR (identify_competitors) ---`); console.log(pythonError); console.log(`-------------------------------------`); if (contextContent) { fs.unlinkSync(tempContextFilePath); } if (code !== 0) { console.error(`Python script (identify_competitors) exited with error.`); return res.status(500).json({ error: 'Python script failed', details: pythonError }); } try { const result = JSON.parse(pythonOutput); res.json(result); } catch (parseError) { console.error('Failed to parse Python output (identify_competitors) as JSON:', parseError); res.status(500).json({ error: 'Invalid JSON from Python script', rawOutput: pythonOutput, details: pythonError }); } }); pythonProcess.on('error', (err) => { console.error('FATAL: Failed to start python process itself (identify_competitors).', err); if (contextContent) { fs.unlinkSync(tempContextFilePath); } res.status(500).json({ error: 'Failed to start Python process', details: err.message }); }); } catch (writeError) { console.error('Failed to write temporary context file (identify_competitors):', writeError); res.status(500).json({ error: 'Failed to write temporary file', details: writeError.message }); } }); // API-Endpunkt für analyze-company (Deep Tech Audit) app.post('/api/analyze-company', async (req, res) => { console.log(`[${new Date().toISOString()}] HIT: /api/analyze-company`); const { companyName, strategy, targetMarket } = req.body; if (!companyName || !strategy) { console.error('Validation Error: Missing companyName or strategy for analyze-company.'); return res.status(400).json({ error: 'Missing companyName or strategy' }); } try { const pythonExecutable = path.join(__dirname, '..', '.venv', 'bin', 'python3'); const pythonScript = path.join(__dirname, '..', 'market_intel_orchestrator.py'); const scriptArgs = [ pythonScript, '--mode', 'analyze_company', '--company_name', companyName, '--strategy_json', JSON.stringify(strategy), '--target_market', targetMarket || 'Germany' ]; console.log(`Spawning Audit for ${companyName}: ${pythonExecutable} ...`); const pythonProcess = spawn(pythonExecutable, scriptArgs, { env: { ...process.env, PYTHONPATH: path.join(__dirname, '..', '.venv', 'lib', 'python3.11', 'site-packages') } }); let pythonOutput = ''; let pythonError = ''; pythonProcess.stdout.on('data', (data) => { pythonOutput += data.toString(); }); pythonProcess.stderr.on('data', (data) => { pythonError += data.toString(); }); pythonProcess.on('close', (code) => { console.log(`Audit for ${companyName} finished with exit code: ${code}`); // Log stderr nur bei Fehler oder wenn nötig, um Logs nicht zu fluten if (pythonError) { console.log(`--- STDERR (Audit ${companyName}) ---`); console.log(pythonError); } if (code !== 0) { console.error(`Python script (analyze_company) exited with error.`); return res.status(500).json({ error: 'Python script failed', details: pythonError }); } try { // Versuche JSON zu parsen. Manchmal gibt Python zusätzlichen Text aus, den wir filtern müssen. // Da wir stderr für Logs nutzen, sollte stdout rein sein. const result = JSON.parse(pythonOutput); res.json(result); } catch (parseError) { console.error('Failed to parse Python output (analyze_company) as JSON:', parseError); console.log('Raw Output:', pythonOutput); res.status(500).json({ error: 'Invalid JSON from Python script', rawOutput: pythonOutput, details: pythonError }); } }); pythonProcess.on('error', (err) => { console.error(`FATAL: Failed to start python process for ${companyName}.`, err); res.status(500).json({ error: 'Failed to start Python process', details: err.message }); }); } catch (err) { console.error(`Internal Server Error in /api/analyze-company: ${err.message}`); res.status(500).json({ error: err.message }); } }); // API-Endpunkt für generate-outreach app.post('/api/generate-outreach', async (req, res) => { console.log(`[${new Date().toISOString()}] HIT: /api/generate-outreach`); const { companyData, knowledgeBase, referenceUrl } = req.body; if (!companyData || !knowledgeBase) { console.error('Validation Error: Missing companyData or knowledgeBase for generate-outreach.'); return res.status(400).json({ error: 'Missing companyData or knowledgeBase' }); } const tempDataFilePath = path.join(__dirname, 'tmp', `outreach_data_${Date.now()}.json`); const tempContextFilePath = path.join(__dirname, 'tmp', `outreach_context_${Date.now()}.md`); const tmpDir = path.join(__dirname, 'tmp'); if (!fs.existsSync(tmpDir)) { fs.mkdirSync(tmpDir); } try { fs.writeFileSync(tempDataFilePath, JSON.stringify(companyData)); fs.writeFileSync(tempContextFilePath, knowledgeBase); console.log(`Successfully wrote temporary files for outreach.`); const pythonExecutable = path.join(__dirname, '..', '.venv', 'bin', 'python3'); const pythonScript = path.join(__dirname, '..', 'market_intel_orchestrator.py'); const scriptArgs = [ pythonScript, '--mode', 'generate_outreach', '--company_data_file', tempDataFilePath, '--context_file', tempContextFilePath, '--reference_url', referenceUrl || '' ]; console.log(`Spawning Outreach Generation for ${companyData.companyName}...`); const pythonProcess = spawn(pythonExecutable, scriptArgs, { env: { ...process.env, PYTHONPATH: path.join(__dirname, '..', '.venv', 'lib', 'python3.11', 'site-packages') } }); let pythonOutput = ''; let pythonError = ''; pythonProcess.stdout.on('data', (data) => { pythonOutput += data.toString(); }); pythonProcess.stderr.on('data', (data) => { pythonError += data.toString(); }); pythonProcess.on('close', (code) => { console.log(`Outreach Generation finished with exit code: ${code}`); // Clean up if (fs.existsSync(tempDataFilePath)) fs.unlinkSync(tempDataFilePath); if (fs.existsSync(tempContextFilePath)) fs.unlinkSync(tempContextFilePath); if (code !== 0) { console.error(`Python script (generate_outreach) exited with error.`); return res.status(500).json({ error: 'Python script failed', details: pythonError }); } try { const result = JSON.parse(pythonOutput); res.json(result); } catch (parseError) { console.error('Failed to parse Python output (generate_outreach) as JSON:', parseError); res.status(500).json({ error: 'Invalid JSON from Python script', rawOutput: pythonOutput, details: pythonError }); } }); pythonProcess.on('error', (err) => { console.error(`FATAL: Failed to start python process for outreach.`, err); if (fs.existsSync(tempDataFilePath)) fs.unlinkSync(tempDataFilePath); if (fs.existsSync(tempContextFilePath)) fs.unlinkSync(tempContextFilePath); res.status(500).json({ error: 'Failed to start Python process', details: err.message }); }); } catch (err) { console.error(`Internal Server Error in /api/generate-outreach: ${err.message}`); res.status(500).json({ error: err.message }); } }); // Start des Servers app.listen(PORT, () => { console.log(`Node.js API Bridge running on http://localhost:${PORT}`); });