255 lines
8.9 KiB
JavaScript
255 lines
8.9 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;
|
|
|
|
// Middleware
|
|
app.use(cors());
|
|
app.use(bodyParser.json({ limit: '50mb' }));
|
|
|
|
// Helper für Python-Aufrufe, um Code-Duplizierung zu vermeiden
|
|
const runPython = (args, res, tempFilesToDelete = []) => {
|
|
// Im Docker (python:3.11-slim Image) nutzen wir das globale python3
|
|
const pythonExecutable = 'python3';
|
|
|
|
console.log(`Spawning command: ${pythonExecutable} ${args.join(' ')}`);
|
|
|
|
const pythonProcess = spawn(pythonExecutable, args);
|
|
|
|
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}`);
|
|
if (pythonOutput.length > 500) {
|
|
console.log(`--- STDOUT (Truncated) ---`);
|
|
console.log(pythonOutput.substring(0, 500) + '...');
|
|
} else {
|
|
console.log(`--- STDOUT ---`);
|
|
console.log(pythonOutput);
|
|
}
|
|
|
|
if (pythonError) {
|
|
console.log(`--- STDERR ---`);
|
|
console.log(pythonError);
|
|
}
|
|
console.log(`----------------`);
|
|
|
|
// Aufräumen
|
|
tempFilesToDelete.forEach(file => {
|
|
if (fs.existsSync(file)) {
|
|
try {
|
|
fs.unlinkSync(file);
|
|
} catch (e) {
|
|
console.error(`Failed to delete temp file ${file}:`, e.message);
|
|
}
|
|
}
|
|
});
|
|
|
|
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);
|
|
tempFilesToDelete.forEach(file => {
|
|
if (fs.existsSync(file)) fs.unlinkSync(file);
|
|
});
|
|
res.status(500).json({ error: 'Failed to start Python process', details: err.message });
|
|
});
|
|
};
|
|
|
|
|
|
// 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) {
|
|
return res.status(400).json({ error: 'Missing referenceUrl or contextContent' });
|
|
}
|
|
|
|
const tmpDir = path.join(__dirname, 'tmp');
|
|
if (!fs.existsSync(tmpDir)) fs.mkdirSync(tmpDir);
|
|
const tempContextFilePath = path.join(tmpDir, `context_${Date.now()}.md`);
|
|
|
|
try {
|
|
fs.writeFileSync(tempContextFilePath, contextContent);
|
|
|
|
const pythonScript = path.join(__dirname, '..', 'market_intel_orchestrator.py');
|
|
const scriptArgs = [pythonScript, '--mode', 'generate_strategy', '--reference_url', referenceUrl, '--context_file', tempContextFilePath];
|
|
|
|
runPython(scriptArgs, res, [tempContextFilePath]);
|
|
|
|
} 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) {
|
|
return res.status(400).json({ error: 'Missing referenceUrl or targetMarket' });
|
|
}
|
|
|
|
const tmpDir = path.join(__dirname, 'tmp');
|
|
if (!fs.existsSync(tmpDir)) fs.mkdirSync(tmpDir);
|
|
const tempContextFilePath = path.join(tmpDir, `context_comp_${Date.now()}.md`);
|
|
|
|
try {
|
|
const cleanupFiles = [];
|
|
if (contextContent) {
|
|
fs.writeFileSync(tempContextFilePath, contextContent);
|
|
cleanupFiles.push(tempContextFilePath);
|
|
}
|
|
|
|
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);
|
|
|
|
runPython(scriptArgs, res, cleanupFiles);
|
|
|
|
} catch (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) {
|
|
return res.status(400).json({ error: 'Missing companyName or strategy' });
|
|
}
|
|
|
|
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'
|
|
];
|
|
|
|
runPython(scriptArgs, res);
|
|
});
|
|
|
|
// API-Endpunkt für generate-outreach
|
|
app.post('/api/generate-outreach', async (req, res) => {
|
|
console.log(`[${new Date().toISOString()}] HIT: /api/generate-outreach`);
|
|
|
|
// Set a long timeout for this specific route (5 minutes)
|
|
req.setTimeout(300000);
|
|
|
|
const { companyData, knowledgeBase, referenceUrl, specific_role } = req.body;
|
|
|
|
if (!companyData || !knowledgeBase) {
|
|
return res.status(400).json({ error: 'Missing companyData or knowledgeBase' });
|
|
}
|
|
|
|
const tmpDir = path.join(__dirname, 'tmp');
|
|
if (!fs.existsSync(tmpDir)) fs.mkdirSync(tmpDir);
|
|
const tempDataFilePath = path.join(tmpDir, `outreach_data_${Date.now()}.json`);
|
|
const tempContextFilePath = path.join(tmpDir, `outreach_context_${Date.now()}.md`);
|
|
|
|
try {
|
|
fs.writeFileSync(tempDataFilePath, JSON.stringify(companyData));
|
|
fs.writeFileSync(tempContextFilePath, knowledgeBase);
|
|
|
|
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 || ''
|
|
];
|
|
|
|
if (specific_role) {
|
|
scriptArgs.push('--specific_role', specific_role);
|
|
}
|
|
|
|
runPython(scriptArgs, res, [tempDataFilePath, tempContextFilePath]);
|
|
|
|
} catch (err) {
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
// --- DATABASE ROUTES ---
|
|
|
|
// Initialize DB on startup
|
|
const dbScript = path.join(__dirname, '..', 'market_db_manager.py');
|
|
spawn('python3', [dbScript, 'init']);
|
|
|
|
app.get('/api/projects', (req, res) => {
|
|
runPython([dbScript, 'list'], res);
|
|
});
|
|
|
|
app.get('/api/projects/:id', (req, res) => {
|
|
runPython([dbScript, 'load', req.params.id], res);
|
|
});
|
|
|
|
app.delete('/api/projects/:id', (req, res) => {
|
|
runPython([dbScript, 'delete', req.params.id], res);
|
|
});
|
|
|
|
app.post('/api/save-project', (req, res) => {
|
|
const projectData = req.body;
|
|
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));
|
|
runPython([dbScript, 'save', tempFilePath], res, [tempFilePath]);
|
|
} catch (e) {
|
|
res.status(500).json({ error: 'Failed to write project data to disk' });
|
|
}
|
|
});
|
|
|
|
const server = app.listen(PORT, () => {
|
|
console.log(`Node.js API Bridge running on http://localhost:${PORT} (Version: 1.1.0-Fix)`);
|
|
});
|
|
|
|
// Set timeout to 10 minutes (600s) to handle long AI generation steps
|
|
server.setTimeout(600000);
|
|
server.keepAliveTimeout = 610000;
|
|
server.headersTimeout = 620000; |