Files
Brancheneinstufung2/general-market-intelligence/server.cjs
Floke 0a00146fd6 feat(market-intel): complete end-to-end audit with enhanced UX and grounding
- Integrated ICP-based lookalike sourcing.
- Implemented Deep Tech Audit with automated evidence collection.
- Enhanced processing terminal with real-time logs.
- Refined daily logging and resolved all dependency issues.
- Documented final status and next steps.
2025-12-21 22:39:30 +00:00

276 lines
11 KiB
JavaScript

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 });
}
});
// Start des Servers
app.listen(PORT, () => {
console.log(`Node.js API Bridge running on http://localhost:${PORT}`);
});