380 lines
14 KiB
JavaScript
380 lines
14 KiB
JavaScript
require('dotenv').config();
|
|
const express = require('express');
|
|
const cors = require('cors');
|
|
const helmet = require('helmet');
|
|
const morgan = require('morgan');
|
|
const http = require('http');
|
|
const { Server } = require('socket.io');
|
|
const axios = require('axios');
|
|
|
|
// Import database
|
|
const database = require('./config/database');
|
|
|
|
// Import routes (we'll create these next)
|
|
const templateRoutes = require('./routes/templates');
|
|
const featureRoutes = require('./routes/features');
|
|
const learningRoutes = require('./routes/learning');
|
|
const adminRoutes = require('./routes/admin');
|
|
const adminTemplateRoutes = require('./routes/admin-templates');
|
|
const techStackRoutes = require('./routes/tech-stack');
|
|
const tkgMigrationRoutes = require('./routes/tkg-migration');
|
|
const autoTKGMigrationRoutes = require('./routes/auto-tkg-migration');
|
|
const ckgMigrationRoutes = require('./routes/ckg-migration');
|
|
const enhancedCkgTechStackRoutes = require('./routes/enhanced-ckg-tech-stack');
|
|
const comprehensiveMigrationRoutes = require('./routes/comprehensive-migration');
|
|
const AdminNotification = require('./models/admin_notification');
|
|
const autoTechStackAnalyzer = require('./services/auto_tech_stack_analyzer');
|
|
const AutoTKGMigrationService = require('./services/auto-tkg-migration');
|
|
const AutoCKGMigrationService = require('./services/auto-ckg-migration');
|
|
// const customTemplateRoutes = require('./routes/custom_templates');
|
|
|
|
const app = express();
|
|
const server = http.createServer(app);
|
|
const io = new Server(server, {
|
|
cors: {
|
|
origin: "*",
|
|
methods: ["GET", "POST"],
|
|
credentials: true
|
|
}
|
|
});
|
|
const PORT = process.env.PORT || 8009;
|
|
|
|
// Middleware
|
|
app.use(helmet());
|
|
app.use(cors({
|
|
origin: "*",
|
|
credentials: true,
|
|
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
|
allowedHeaders: ['Content-Type', 'Authorization', 'X-User-ID', 'X-User-Role']
|
|
}));
|
|
app.use(morgan('combined'));
|
|
app.use(express.json({ limit: '10mb' }));
|
|
app.use(express.urlencoded({ extended: true }));
|
|
|
|
// Make io available to routes and set it in AdminNotification
|
|
app.set('io', io);
|
|
AdminNotification.setSocketIO(io);
|
|
|
|
// Routes - Order matters! More specific routes should come first
|
|
app.use('/api/learning', learningRoutes);
|
|
app.use('/api/admin', adminRoutes);
|
|
app.use('/api/admin/templates', adminTemplateRoutes);
|
|
app.use('/api/tech-stack', techStackRoutes);
|
|
app.use('/api/enhanced-ckg-tech-stack', enhancedCkgTechStackRoutes);
|
|
app.use('/api/tkg-migration', tkgMigrationRoutes);
|
|
app.use('/api/auto-tkg-migration', autoTKGMigrationRoutes);
|
|
app.use('/api/ckg-migration', ckgMigrationRoutes);
|
|
app.use('/api/comprehensive-migration', comprehensiveMigrationRoutes);
|
|
app.use('/api/templates', templateRoutes);
|
|
// Add admin routes under /api/templates to match serviceClient expectations
|
|
app.use('/api/templates/admin', adminRoutes);
|
|
// Features route must come AFTER templates to avoid route conflicts
|
|
app.use('/api/features', featureRoutes);
|
|
// Single route surface: handle custom templates via /api/templates only
|
|
// app.use('/api/custom-templates', customTemplateRoutes);
|
|
|
|
// WebSocket connection handling
|
|
io.on('connection', async (socket) => {
|
|
console.log('🔌 Admin client connected:', socket.id);
|
|
|
|
// Join admin room for notifications
|
|
socket.join('admin-notifications');
|
|
|
|
// Send initial notification count
|
|
try {
|
|
const counts = await AdminNotification.getCounts();
|
|
socket.emit('notification-count', counts);
|
|
} catch (error) {
|
|
console.error('Error getting notification counts:', error);
|
|
socket.emit('notification-count', { total: 0, unread: 0, read: 0 });
|
|
}
|
|
|
|
socket.on('disconnect', () => {
|
|
console.log('🔌 Admin client disconnected:', socket.id);
|
|
});
|
|
});
|
|
|
|
// Health check endpoint
|
|
app.get('/health', (req, res) => {
|
|
res.status(200).json({
|
|
status: 'healthy',
|
|
service: 'template-manager',
|
|
version: '1.0.0',
|
|
timestamp: new Date().toISOString(),
|
|
uptime: process.uptime(),
|
|
features: {
|
|
template_management: true,
|
|
feature_learning: true,
|
|
usage_tracking: true,
|
|
self_improving: true,
|
|
admin_approval_workflow: true,
|
|
ai_analysis: true
|
|
}
|
|
});
|
|
});
|
|
|
|
// AI Feature Analysis endpoint
|
|
app.post('/api/analyze-feature', async (req, res) => {
|
|
try {
|
|
console.log('🤖 [Template Manager] AI Analysis request received');
|
|
|
|
const { featureName, feature_name, description, requirements = [], projectType, project_type } = req.body;
|
|
|
|
// Handle both parameter name variations
|
|
const actualFeatureName = featureName || feature_name;
|
|
const actualProjectType = projectType || project_type;
|
|
|
|
// Ensure requirements is always an array
|
|
const safeRequirements = Array.isArray(requirements) ? requirements : [];
|
|
|
|
console.log('📋 [Template Manager] Analyzing feature:', actualFeatureName);
|
|
console.log('📋 [Template Manager] Project type:', actualProjectType);
|
|
console.log('📋 [Template Manager] Requirements:', safeRequirements);
|
|
// Always use Claude. No rule-based fallback.
|
|
console.log('🤖 [Template Manager] Using Claude AI for analysis (no fallback)...');
|
|
const analysis = await analyzeWithClaude(actualFeatureName, description, safeRequirements, actualProjectType);
|
|
console.log('✅ [Template Manager] Analysis completed:', analysis?.complexity, 'complexity');
|
|
console.log('🧩 [Template Manager] logicRules:', Array.isArray(analysis?.logicRules) ? analysis.logicRules : 'none');
|
|
|
|
res.json({ success: true, analysis });
|
|
|
|
} catch (error) {
|
|
console.error('❌ [Template Manager] AI Analysis error:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: error.message,
|
|
message: 'AI analysis failed'
|
|
});
|
|
}
|
|
});
|
|
|
|
// Claude AI Analysis function
|
|
async function analyzeWithClaude(featureName, description, requirements, projectType) {
|
|
const CLAUDE_API_KEY = process.env.CLAUDE_API_KEY;
|
|
|
|
// If no API key, return a stub analysis instead of making API calls
|
|
if (!CLAUDE_API_KEY) {
|
|
console.warn('[Template Manager] No Claude API key, returning stub analysis');
|
|
const safeRequirements = Array.isArray(requirements) ? requirements : [];
|
|
return {
|
|
feature_name: featureName || 'Custom Feature',
|
|
complexity: 'medium',
|
|
logicRules: [
|
|
'Only admins can access advanced dashboard metrics',
|
|
'Validate inputs for financial operations and POS entries',
|
|
'Enforce role-based access for multi-user actions'
|
|
],
|
|
implementation_details: [
|
|
'Use RBAC middleware for protected routes',
|
|
'Queue long-running analytics jobs',
|
|
'Paginate and cache dashboard queries'
|
|
],
|
|
technical_requirements: safeRequirements.length ? safeRequirements : [
|
|
'Relational DB for transactions and inventory',
|
|
'Real-time updates via websockets',
|
|
'Background worker for analytics'
|
|
],
|
|
estimated_effort: '2-3 weeks',
|
|
dependencies: ['Auth service', 'Payments gateway integration'],
|
|
api_endpoints: ['POST /api/transactions', 'GET /api/dashboard/metrics'],
|
|
database_tables: ['transactions', 'inventory', 'customers'],
|
|
confidence_score: 0.5
|
|
};
|
|
}
|
|
|
|
const safeRequirements = Array.isArray(requirements) ? requirements : [];
|
|
const requirementsText = safeRequirements.length > 0 ? safeRequirements.map(req => `- ${req}`).join('\n') : 'No specific requirements provided';
|
|
|
|
const prompt = `Analyze this custom feature for a ${projectType || 'web application'} project:
|
|
|
|
Feature Name: ${featureName || 'Custom Feature'}
|
|
Description: ${description || 'No description provided'}
|
|
|
|
Detailed Requirements:
|
|
${requirementsText}
|
|
|
|
Based on these requirements, provide a detailed analysis in JSON format:
|
|
{
|
|
"feature_name": "Improved technical name",
|
|
"complexity": "low|medium|high",
|
|
"logicRules": ["Business rule 1", "Business rule 2", "Business rule 3"],
|
|
"implementation_details": ["Technical detail 1", "Technical detail 2", "Technical detail 3"],
|
|
"technical_requirements": ["Requirement 1", "Requirement 2", "Requirement 3"],
|
|
"estimated_effort": "1-2 weeks|2-3 weeks|3-4 weeks|4+ weeks",
|
|
"dependencies": ["Dependency 1", "Dependency 2"],
|
|
"api_endpoints": ["POST /api/endpoint1", "GET /api/endpoint2"],
|
|
"database_tables": ["table1", "table2"],
|
|
"confidence_score": 0.85
|
|
}
|
|
|
|
For complexity assessment:
|
|
- "low": Simple CRUD, basic features, minimal business logic
|
|
- "medium": Moderate business logic, some integrations, standard features
|
|
- "high": Complex business rules, external integrations, security requirements, real-time features
|
|
|
|
For logicRules, generate specific business rules based on the requirements like:
|
|
- Access control and authorization rules
|
|
- Data validation and business logic rules
|
|
- Workflow and process rules
|
|
- Security and compliance requirements
|
|
|
|
Return ONLY the JSON object, no other text.`;
|
|
|
|
try {
|
|
console.log('🔍 [Template Manager] Making Claude API request...');
|
|
console.log('🔍 [Template Manager] API Key length:', CLAUDE_API_KEY ? CLAUDE_API_KEY.length : 0);
|
|
console.log('🔍 [Template Manager] Prompt length:', prompt.length);
|
|
|
|
const response = await axios.post('https://api.anthropic.com/v1/messages', {
|
|
model: 'claude-3-5-sonnet-20241022',
|
|
max_tokens: 2000,
|
|
temperature: 0.1,
|
|
messages: [
|
|
{
|
|
role: 'user',
|
|
content: [
|
|
{ type: 'text', text: prompt }
|
|
]
|
|
}
|
|
]
|
|
}, {
|
|
headers: {
|
|
'x-api-key': CLAUDE_API_KEY,
|
|
'Content-Type': 'application/json',
|
|
'anthropic-version': '2023-06-01'
|
|
},
|
|
timeout: 30000
|
|
});
|
|
|
|
console.log('✅ [Template Manager] Claude API response received');
|
|
console.log('🔍 [Template Manager] Response status:', response.status);
|
|
|
|
const responseText = (response?.data?.content?.[0]?.text || '').trim();
|
|
console.log('🔍 [Template Manager] Raw Claude response:', responseText.substring(0, 200) + '...');
|
|
|
|
// Extract JSON from response
|
|
const jsonMatch = responseText.match(/\{[\s\S]*\}/);
|
|
if (jsonMatch) {
|
|
const analysis = JSON.parse(jsonMatch[0]);
|
|
console.log('✅ [Template Manager] Claude analysis successful');
|
|
console.log('🔍 [Template Manager] Parsed analysis:', JSON.stringify(analysis, null, 2));
|
|
return analysis;
|
|
} else {
|
|
// Hard fail if Claude returns non-JSON; do not fallback
|
|
console.error('❌ [Template Manager] No valid JSON found in Claude response');
|
|
console.error('🔍 [Template Manager] Full response:', responseText);
|
|
throw new Error('No valid JSON found in Claude response');
|
|
}
|
|
} catch (error) {
|
|
// Surface provider message to aid debugging
|
|
const providerMessage = error.response?.data?.error?.message || error.response?.data || error.message;
|
|
console.error('❌ [Template Manager] Claude API error:', providerMessage);
|
|
throw new Error(`Claude API error: ${providerMessage}`);
|
|
}
|
|
}
|
|
|
|
// Removed rule-based fallback and helpers. Claude is mandatory.
|
|
|
|
// Root endpoint
|
|
app.get('/', (req, res) => {
|
|
res.json({
|
|
message: 'Template Manager Service - Self-Learning Feature Database',
|
|
version: '1.0.0',
|
|
endpoints: {
|
|
health: '/health',
|
|
templates: '/api/templates',
|
|
features: '/api/features',
|
|
learning: '/api/learning',
|
|
admin: '/api/admin',
|
|
techStack: '/api/tech-stack',
|
|
enhancedCkgTechStack: '/api/enhanced-ckg-tech-stack',
|
|
tkgMigration: '/api/tkg-migration',
|
|
ckgMigration: '/api/ckg-migration',
|
|
customTemplates: '/api/custom-templates'
|
|
}
|
|
});
|
|
});
|
|
|
|
// Error handling middleware
|
|
app.use((err, req, res, next) => {
|
|
console.error('❌ Error:', err.stack);
|
|
res.status(500).json({
|
|
error: 'Internal Server Error',
|
|
message: process.env.NODE_ENV === 'development' ? err.message : 'Something went wrong'
|
|
});
|
|
});
|
|
|
|
// 404 handler
|
|
app.use('*', (req, res) => {
|
|
res.status(404).json({
|
|
error: 'Not Found',
|
|
message: `Route ${req.originalUrl} not found`
|
|
});
|
|
});
|
|
|
|
// Graceful shutdown
|
|
process.on('SIGINT', async () => {
|
|
console.log('🛑 Shutting down Template Manager...');
|
|
await database.close();
|
|
process.exit(0);
|
|
});
|
|
|
|
// Start server
|
|
server.listen(PORT, '0.0.0.0', async () => {
|
|
console.log('🚀 Template Manager Service started');
|
|
console.log(`📡 Server running on http://0.0.0.0:${PORT}`);
|
|
console.log(`🏥 Health check: http://0.0.0.0:${PORT}/health`);
|
|
console.log('🔌 WebSocket server ready for real-time notifications');
|
|
console.log('🎯 Self-learning feature database ready!');
|
|
|
|
// Initialize automated tech stack analyzer
|
|
try {
|
|
console.log('🤖 Initializing automated tech stack analyzer...');
|
|
await autoTechStackAnalyzer.initialize();
|
|
console.log('✅ Automated tech stack analyzer initialized successfully');
|
|
|
|
// Start analyzing existing templates in background
|
|
console.log('🔍 Starting background analysis of existing templates...');
|
|
setTimeout(async () => {
|
|
try {
|
|
const result = await autoTechStackAnalyzer.analyzeAllPendingTemplates();
|
|
console.log(`🎉 Background analysis completed: ${result.message}`);
|
|
} catch (error) {
|
|
console.error('⚠️ Background analysis failed:', error.message);
|
|
}
|
|
}, 5000); // Wait 5 seconds after startup
|
|
|
|
} catch (error) {
|
|
console.error('❌ Failed to initialize automated tech stack analyzer:', error.message);
|
|
}
|
|
|
|
// Initialize automated TKG migration service
|
|
try {
|
|
console.log('🔄 Initializing automated TKG migration service...');
|
|
const autoTKGMigration = new AutoTKGMigrationService();
|
|
await autoTKGMigration.initialize();
|
|
console.log('✅ Automated TKG migration service initialized successfully');
|
|
|
|
// Make auto-migration service available globally
|
|
app.set('autoTKGMigration', autoTKGMigration);
|
|
|
|
} catch (error) {
|
|
console.error('❌ Failed to initialize automated TKG migration service:', error.message);
|
|
}
|
|
|
|
// Initialize automated CKG migration service
|
|
try {
|
|
console.log('🔄 Initializing automated CKG migration service...');
|
|
const autoCKGMigration = new AutoCKGMigrationService();
|
|
await autoCKGMigration.initialize();
|
|
console.log('✅ Automated CKG migration service initialized successfully');
|
|
|
|
// Make auto-migration service available globally
|
|
app.set('autoCKGMigration', autoCKGMigration);
|
|
|
|
} catch (error) {
|
|
console.error('❌ Failed to initialize automated CKG migration service:', error.message);
|
|
}
|
|
});
|
|
|
|
module.exports = app; |