codenuk_backend_mine/services/template-manager/src/routes/tech-stack.js
2025-10-10 08:56:39 +05:30

626 lines
20 KiB
JavaScript

const express = require('express');
const router = express.Router();
const TechStackRecommendation = require('../models/tech_stack_recommendation');
const IntelligentTechStackAnalyzer = require('../services/intelligent-tech-stack-analyzer');
const autoTechStackAnalyzer = require('../services/auto_tech_stack_analyzer');
const Template = require('../models/template');
const CustomTemplate = require('../models/custom_template');
const Feature = require('../models/feature');
const CustomFeature = require('../models/custom_feature');
const database = require('../config/database');
// Initialize analyzer
const analyzer = new IntelligentTechStackAnalyzer();
// GET /api/tech-stack/recommendations - Get all tech stack recommendations
router.get('/recommendations', async (req, res) => {
try {
const limit = parseInt(req.query.limit) || 50;
const offset = parseInt(req.query.offset) || 0;
const status = req.query.status || null;
console.log(`📊 [TechStack] Fetching recommendations (status: ${status || 'all'}, limit: ${limit}, offset: ${offset})`);
let recommendations;
if (status) {
recommendations = await TechStackRecommendation.getByStatus(status, limit, offset);
} else {
recommendations = await TechStackRecommendation.getAll(limit, offset);
}
res.json({
success: true,
data: recommendations,
count: recommendations.length,
message: `Found ${recommendations.length} tech stack recommendations`
});
} catch (error) {
console.error('❌ Error fetching tech stack recommendations:', error.message);
res.status(500).json({
success: false,
error: 'Failed to fetch recommendations',
message: error.message
});
}
});
// GET /api/tech-stack/recommendations/with-details - Get recommendations with template details
router.get('/recommendations/with-details', async (req, res) => {
try {
const limit = parseInt(req.query.limit) || 50;
const offset = parseInt(req.query.offset) || 0;
console.log(`📊 [TechStack] Fetching recommendations with template details (limit: ${limit}, offset: ${offset})`);
const recommendations = await TechStackRecommendation.getWithTemplateDetails(limit, offset);
res.json({
success: true,
data: recommendations,
count: recommendations.length,
message: `Found ${recommendations.length} recommendations with template details`
});
} catch (error) {
console.error('❌ Error fetching recommendations with details:', error.message);
res.status(500).json({
success: false,
error: 'Failed to fetch recommendations with details',
message: error.message
});
}
});
// GET /api/tech-stack/recommendations/:templateId - Get recommendation for specific template
router.get('/recommendations/:templateId', async (req, res) => {
try {
const { templateId } = req.params;
const templateType = req.query.templateType || null;
console.log(`🔍 [TechStack] Fetching recommendation for template: ${templateId} (type: ${templateType || 'any'})`);
const recommendation = await TechStackRecommendation.getByTemplateId(templateId, templateType);
if (!recommendation) {
return res.status(404).json({
success: false,
error: 'Recommendation not found',
message: `No tech stack recommendation found for template ${templateId}`
});
}
res.json({
success: true,
data: recommendation,
message: `Tech stack recommendation found for template ${templateId}`
});
} catch (error) {
console.error('❌ Error fetching recommendation:', error.message);
res.status(500).json({
success: false,
error: 'Failed to fetch recommendation',
message: error.message
});
}
});
// POST /api/tech-stack/analyze/:templateId - Analyze specific template
router.post('/analyze/:templateId', async (req, res) => {
try {
const { templateId } = req.params;
const forceUpdate = req.query.force === 'true';
console.log(`🤖 [TechStack] Starting analysis for template: ${templateId} (force: ${forceUpdate})`);
// Check if recommendation already exists
if (!forceUpdate) {
const existing = await TechStackRecommendation.getByTemplateId(templateId);
if (existing) {
return res.json({
success: true,
data: existing,
message: `Recommendation already exists for template ${templateId}. Use ?force=true to update.`,
cached: true
});
}
}
// Fetch template with features and business rules
const templateData = await fetchTemplateWithFeatures(templateId);
if (!templateData) {
return res.status(404).json({
success: false,
error: 'Template not found',
message: `Template with ID ${templateId} does not exist`
});
}
// Analyze template
const analysisResult = await analyzer.analyzeTemplate(templateData);
// Save recommendation
const recommendation = await TechStackRecommendation.upsert(
templateId,
templateData.is_custom ? 'custom' : 'default',
analysisResult
);
res.json({
success: true,
data: recommendation,
message: `Tech stack analysis completed for template ${templateData.title}`,
cached: false
});
} catch (error) {
console.error('❌ Error analyzing template:', error.message);
res.status(500).json({
success: false,
error: 'Analysis failed',
message: error.message
});
}
});
// POST /api/tech-stack/analyze/batch - Batch analyze all templates
router.post('/analyze/batch', async (req, res) => {
try {
const {
forceUpdate = false,
templateIds = null,
includeCustom = true,
includeDefault = true
} = req.body;
console.log(`🚀 [TechStack] Starting batch analysis (force: ${forceUpdate}, custom: ${includeCustom}, default: ${includeDefault})`);
// Fetch all templates with features
const templates = await fetchAllTemplatesWithFeatures(includeCustom, includeDefault, templateIds);
if (templates.length === 0) {
return res.json({
success: true,
data: [],
message: 'No templates found for analysis',
summary: { total: 0, processed: 0, failed: 0 }
});
}
console.log(`📊 [TechStack] Found ${templates.length} templates for analysis`);
// Filter out templates that already have recommendations (unless force update)
let templatesToAnalyze = templates;
if (!forceUpdate) {
const existingRecommendations = await Promise.all(
templates.map(t => TechStackRecommendation.getByTemplateId(t.id))
);
templatesToAnalyze = templates.filter((template, index) => !existingRecommendations[index]);
console.log(`📊 [TechStack] ${templates.length - templatesToAnalyze.length} templates already have recommendations`);
}
if (templatesToAnalyze.length === 0) {
return res.json({
success: true,
data: [],
message: 'All templates already have recommendations. Use forceUpdate=true to re-analyze.',
summary: { total: templates.length, processed: 0, failed: 0, skipped: templates.length }
});
}
// Start batch analysis
const results = await analyzer.batchAnalyze(templatesToAnalyze, (current, total, title, status) => {
console.log(`📈 [TechStack] Progress: ${current}/${total} - ${title} (${status})`);
});
// Save all results
const savedRecommendations = [];
const failedRecommendations = [];
for (const result of results) {
try {
const recommendation = await TechStackRecommendation.upsert(
result.template_id,
result.template_type,
result
);
savedRecommendations.push(recommendation);
} catch (saveError) {
console.error(`❌ Failed to save recommendation for ${result.template_id}:`, saveError.message);
failedRecommendations.push({
template_id: result.template_id,
error: saveError.message
});
}
}
const summary = {
total: templates.length,
processed: templatesToAnalyze.length,
successful: savedRecommendations.length,
failed: failedRecommendations.length,
skipped: templates.length - templatesToAnalyze.length
};
res.json({
success: true,
data: savedRecommendations,
failed: failedRecommendations,
summary,
message: `Batch analysis completed: ${summary.successful} successful, ${summary.failed} failed, ${summary.skipped} skipped`
});
} catch (error) {
console.error('❌ Error in batch analysis:', error.message);
res.status(500).json({
success: false,
error: 'Batch analysis failed',
message: error.message
});
}
});
// GET /api/tech-stack/stats - Get statistics
router.get('/stats', async (req, res) => {
try {
console.log('📊 [TechStack] Fetching statistics...');
const stats = await TechStackRecommendation.getStats();
res.json({
success: true,
data: stats,
message: 'Tech stack statistics retrieved successfully'
});
} catch (error) {
console.error('❌ Error fetching stats:', error.message);
res.status(500).json({
success: false,
error: 'Failed to fetch statistics',
message: error.message
});
}
});
// GET /api/tech-stack/stale - Get recommendations that need updating
router.get('/stale', async (req, res) => {
try {
const daysOld = parseInt(req.query.days) || 30;
const limit = parseInt(req.query.limit) || 100;
console.log(`📊 [TechStack] Fetching stale recommendations (older than ${daysOld} days, limit: ${limit})`);
const staleRecommendations = await TechStackRecommendation.getStaleRecommendations(daysOld, limit);
res.json({
success: true,
data: staleRecommendations,
count: staleRecommendations.length,
message: `Found ${staleRecommendations.length} recommendations older than ${daysOld} days`
});
} catch (error) {
console.error('❌ Error fetching stale recommendations:', error.message);
res.status(500).json({
success: false,
error: 'Failed to fetch stale recommendations',
message: error.message
});
}
});
// DELETE /api/tech-stack/recommendations/:id - Delete recommendation
router.delete('/recommendations/:id', async (req, res) => {
try {
const { id } = req.params;
console.log(`🗑️ [TechStack] Deleting recommendation: ${id}`);
const deleted = await TechStackRecommendation.delete(id);
if (!deleted) {
return res.status(404).json({
success: false,
error: 'Recommendation not found',
message: `Recommendation with ID ${id} does not exist`
});
}
res.json({
success: true,
message: `Recommendation ${id} deleted successfully`
});
} catch (error) {
console.error('❌ Error deleting recommendation:', error.message);
res.status(500).json({
success: false,
error: 'Failed to delete recommendation',
message: error.message
});
}
});
// POST /api/tech-stack/auto-analyze/all - Automatically analyze all templates without recommendations
router.post('/auto-analyze/all', async (req, res) => {
try {
console.log('🤖 [TechStack] 🚀 Starting auto-analysis for all templates without recommendations...');
const result = await autoTechStackAnalyzer.analyzeAllPendingTemplates();
res.json({
success: true,
data: result,
message: result.message
});
} catch (error) {
console.error('❌ Error in auto-analysis:', error.message);
res.status(500).json({
success: false,
error: 'Auto-analysis failed',
message: error.message
});
}
});
// POST /api/tech-stack/auto-analyze/force-all - Force analyze ALL templates regardless of existing recommendations
router.post('/auto-analyze/force-all', async (req, res) => {
try {
console.log('🤖 [TechStack] 🚀 Starting FORCE analysis for ALL templates...');
const result = await autoTechStackAnalyzer.analyzeAllTemplates(true);
res.json({
success: true,
data: result,
message: result.message
});
} catch (error) {
console.error('❌ Error in force auto-analysis:', error.message);
res.status(500).json({
success: false,
error: 'Force auto-analysis failed',
message: error.message
});
}
});
// POST /api/tech-stack/analyze-existing - Analyze all existing templates in database (including those with old recommendations)
router.post('/analyze-existing', async (req, res) => {
try {
const { forceUpdate = false, daysOld = 30 } = req.body;
console.log(`🤖 [TechStack] 🔍 Starting analysis of existing templates (force: ${forceUpdate}, daysOld: ${daysOld})...`);
// Get all templates from database
const allTemplates = await fetchAllTemplatesWithFeatures(true, true);
console.log(`📊 [TechStack] 📊 Found ${allTemplates.length} total templates in database`);
if (allTemplates.length === 0) {
return res.json({
success: true,
data: { total: 0, queued: 0, skipped: 0 },
message: 'No templates found in database'
});
}
let queuedCount = 0;
let skippedCount = 0;
// Process each template
for (const template of allTemplates) {
const templateType = template.is_custom ? 'custom' : 'default';
if (!forceUpdate) {
// Check if recommendation exists and is recent
const existing = await TechStackRecommendation.getByTemplateId(template.id, templateType);
if (existing && autoTechStackAnalyzer.isRecentRecommendation(existing, daysOld)) {
console.log(`⏭️ [TechStack] ⏸️ Skipping ${template.title} - recent recommendation exists`);
skippedCount++;
continue;
}
}
// Queue for analysis
console.log(`📝 [TechStack] 📝 Queuing existing template: ${template.title} (${templateType})`);
autoTechStackAnalyzer.queueForAnalysis(template.id, templateType, 2); // Normal priority
queuedCount++;
}
const result = {
total: allTemplates.length,
queued: queuedCount,
skipped: skippedCount,
forceUpdate
};
console.log(`✅ [TechStack] ✅ Existing templates analysis queued: ${queuedCount} queued, ${skippedCount} skipped`);
res.json({
success: true,
data: result,
message: `Queued ${queuedCount} existing templates for analysis (${skippedCount} skipped)`
});
} catch (error) {
console.error('❌ Error analyzing existing templates:', error.message);
res.status(500).json({
success: false,
error: 'Failed to analyze existing templates',
message: error.message
});
}
});
// GET /api/tech-stack/auto-analyze/queue - Get automation queue status
router.get('/auto-analyze/queue', async (req, res) => {
try {
const queueStatus = autoTechStackAnalyzer.getQueueStatus();
res.json({
success: true,
data: queueStatus,
message: `Queue status: ${queueStatus.isProcessing ? 'processing' : 'idle'}, ${queueStatus.queueLength} items queued`
});
} catch (error) {
console.error('❌ Error getting queue status:', error.message);
res.status(500).json({
success: false,
error: 'Failed to get queue status',
message: error.message
});
}
});
// POST /api/tech-stack/auto-analyze/queue/clear - Clear the processing queue
router.post('/auto-analyze/queue/clear', async (req, res) => {
try {
const clearedCount = autoTechStackAnalyzer.clearQueue();
res.json({
success: true,
data: { clearedCount },
message: `Cleared ${clearedCount} items from processing queue`
});
} catch (error) {
console.error('❌ Error clearing queue:', error.message);
res.status(500).json({
success: false,
error: 'Failed to clear queue',
message: error.message
});
}
});
// POST /api/tech-stack/auto-analyze/trigger/:templateId - Manually trigger auto-analysis for specific template
router.post('/auto-analyze/trigger/:templateId', async (req, res) => {
try {
const { templateId } = req.params;
const { templateType = null, priority = 1 } = req.body;
console.log(`🤖 [TechStack] Manually triggering auto-analysis for template: ${templateId}`);
// Queue for analysis
autoTechStackAnalyzer.queueForAnalysis(templateId, templateType, priority);
res.json({
success: true,
data: { templateId, templateType, priority },
message: `Template ${templateId} queued for auto-analysis with priority ${priority}`
});
} catch (error) {
console.error('❌ Error triggering auto-analysis:', error.message);
res.status(500).json({
success: false,
error: 'Failed to trigger auto-analysis',
message: error.message
});
}
});
// Helper function to fetch template with features and business rules
async function fetchTemplateWithFeatures(templateId) {
try {
// Check if template exists in default templates
let template = await Template.getByIdWithFeatures(templateId);
let isCustom = false;
if (!template) {
// Check custom templates
template = await CustomTemplate.getByIdWithFeatures(templateId);
isCustom = true;
}
if (!template) {
return null;
}
// Get features and business rules
const features = await Feature.getByTemplateId(templateId);
// Extract business rules
const businessRules = {};
features.forEach(feature => {
if (feature.additional_business_rules) {
businessRules[feature.id] = feature.additional_business_rules;
}
});
return {
...template,
features,
business_rules: businessRules,
feature_count: features.length,
is_custom: isCustom
};
} catch (error) {
console.error('❌ Error fetching template with features:', error.message);
throw error;
}
}
// Helper function to fetch all templates with features
async function fetchAllTemplatesWithFeatures(includeCustom = true, includeDefault = true, templateIds = null) {
try {
const templates = [];
if (includeDefault) {
const defaultTemplates = await Template.getAllByCategory();
const defaultTemplatesFlat = Object.values(defaultTemplates).flat();
templates.push(...defaultTemplatesFlat);
}
if (includeCustom) {
const customTemplates = await CustomTemplate.getAll(1000, 0);
templates.push(...customTemplates);
}
// Filter by template IDs if provided
let filteredTemplates = templates;
if (templateIds && Array.isArray(templateIds)) {
filteredTemplates = templates.filter(t => templateIds.includes(t.id));
}
// Fetch features for each template
const templatesWithFeatures = await Promise.all(
filteredTemplates.map(async (template) => {
try {
const features = await Feature.getByTemplateId(template.id);
// Extract business rules
const businessRules = {};
features.forEach(feature => {
if (feature.additional_business_rules) {
businessRules[feature.id] = feature.additional_business_rules;
}
});
return {
...template,
features,
business_rules: businessRules,
feature_count: features.length,
is_custom: !template.is_active
};
} catch (error) {
console.error(`⚠️ Error fetching features for template ${template.id}:`, error.message);
return {
...template,
features: [],
business_rules: {},
feature_count: 0,
is_custom: !template.is_active,
error: error.message
};
}
})
);
return templatesWithFeatures;
} catch (error) {
console.error('❌ Error fetching all templates with features:', error.message);
throw error;
}
}
module.exports = router;