Kraft102's picture
Initial deployment - WidgeTDC Cortex Backend v2.1.0
529090e
import { Router } from 'express';
import type { Request, Response } from 'express';
import multer from 'multer';
import path from 'path';
const router = Router();
// Configure multer for file uploads
const upload = multer({
storage: multer.memoryStorage(),
limits: {
fileSize: 10 * 1024 * 1024, // 10MB limit
},
fileFilter: (_req, file, cb) => {
// Accept code files and documents
const allowedExtensions = ['.js', '.ts', '.tsx', '.jsx', '.py', '.java', '.cs', '.go', '.md', '.txt', '.pdf', '.docx'];
const ext = path.extname(file.originalname).toLowerCase();
if (allowedExtensions.includes(ext)) {
cb(null, true);
} else {
cb(new Error('Invalid file type. Allowed: ' + allowedExtensions.join(', ')));
}
}
});
interface Finding {
file: string;
line: number;
severity: 'critical' | 'high' | 'medium' | 'low';
category: string;
description: string;
remediation: string;
}
interface AnalysisResult {
summary: {
criticalIssues: number;
highIssues: number;
mediumIssues: number;
lowIssues: number;
totalFiles: number;
};
findings: Finding[];
overallScore: number;
}
interface PersonaFeedback {
persona: string;
role: string;
feedback: string;
confidence: number;
recommendations: string[];
concerns: string[];
}
interface ReviewResult {
summary: string;
consensus: string[];
disagreements: string[];
personas: PersonaFeedback[];
overallScore: number;
}
// Code Analysis Endpoint
router.post('/analyze', upload.single('file'), async (req: Request, res: Response) => {
try {
if (!req.file) {
return res.status(400).json({
error: 'No file uploaded',
message: 'Please upload a code file for analysis'
});
}
const file = req.file;
const fileContent = file.buffer?.toString('utf-8') ?? '';
const fileName = file.originalname;
console.log('Code analysis requested:', {
fileName,
fileSize: file.size,
mimeType: file.mimetype
});
// TODO: Integrate with actual code analysis service (e.g., ESLint, SonarQube, custom security scanner)
// For now, return mock analysis results
if (!fileContent) {
return res.status(400).json({
error: 'Empty file',
message: 'Uploaded file does not contain any readable content'
});
}
// Simulate finding issues based on common patterns
const findings: Finding[] = [];
const lines = fileContent.split('\n');
// Mock security checks
lines.forEach((line, index) => {
if (line.includes('eval(') || line.includes('innerHTML')) {
findings.push({
file: fileName,
line: index + 1,
severity: 'critical',
category: 'Security - XSS',
description: 'Potential XSS vulnerability detected',
remediation: 'Use secure alternatives like textContent or sanitize user input'
});
}
if (line.includes('SELECT * FROM') && line.includes('+')) {
findings.push({
file: fileName,
line: index + 1,
severity: 'critical',
category: 'Security - SQL Injection',
description: 'Potential SQL injection vulnerability',
remediation: 'Use parameterized queries or prepared statements'
});
}
if (line.includes('console.log(')) {
findings.push({
file: fileName,
line: index + 1,
severity: 'low',
category: 'Code Quality',
description: 'Console.log statement found',
remediation: 'Remove console.log or replace with proper logging'
});
}
if (line.includes('any')) {
findings.push({
file: fileName,
line: index + 1,
severity: 'medium',
category: 'Type Safety',
description: 'Use of "any" type reduces type safety',
remediation: 'Use specific types instead of any'
});
}
});
// Calculate summary
const summary = {
criticalIssues: findings.filter(f => f.severity === 'critical').length,
highIssues: findings.filter(f => f.severity === 'high').length,
mediumIssues: findings.filter(f => f.severity === 'medium').length,
lowIssues: findings.filter(f => f.severity === 'low').length,
totalFiles: 1
};
// Calculate overall score (0-100)
const totalIssues = summary.criticalIssues + summary.highIssues + summary.mediumIssues + summary.lowIssues;
const weightedScore =
(summary.criticalIssues * 20) +
(summary.highIssues * 10) +
(summary.mediumIssues * 5) +
(summary.lowIssues * 2);
const overallScore = Math.max(0, Math.min(100, 100 - weightedScore));
const result: AnalysisResult = {
summary,
findings: findings.slice(0, 20), // Limit to 20 findings
overallScore
};
res.json(result);
} catch (error) {
console.error('Code analysis error:', error);
res.status(500).json({
error: 'Analysis failed',
message: error instanceof Error ? error.message : 'Unknown error occurred'
});
}
});
// Spec Panel Multi-Expert Review Endpoint
router.post('/spec-panel', upload.single('file'), async (req: Request, res: Response) => {
try {
if (!req.file) {
return res.status(400).json({
error: 'No file uploaded',
message: 'Please upload a specification document for review'
});
}
const file = req.file;
const fileContent = file.buffer.toString('utf-8');
const fileName = file.originalname;
// Parse personas from request
let selectedPersonas: string[] = [];
try {
selectedPersonas = req.body.personas
? JSON.parse(req.body.personas)
: ['architecture', 'security'];
} catch (e) {
selectedPersonas = ['architecture', 'security'];
}
console.log('Spec panel review requested:', {
fileName,
fileSize: file.size,
personas: selectedPersonas
});
// TODO: Integrate with actual multi-expert review system (e.g., CGentCore business panel)
// For now, return mock expert feedback
const personaFeedbackMap: Record<string, PersonaFeedback> = {
architecture: {
persona: 'architecture',
role: 'Architecture Expert',
feedback: 'Systemdesignet viser god separation of concerns. Dog mangler der detaljer omkring skalering og fejlhåndtering ved høj belastning.',
confidence: 0.82,
recommendations: [
'Implementér circuit breaker pattern til eksterne API-kald',
'Overvej event-driven arkitektur for asynkron kommunikation',
'Tilføj caching-lag for at reducere database-belastning'
],
concerns: [
'Manglende beskrivelse af disaster recovery strategi',
'Uklart hvordan microservices kommunikerer ved netværksfejl'
]
},
security: {
persona: 'security',
role: 'Security Expert',
feedback: 'Sikkerhedsaspekterne er overordnet acceptable, men der mangler konkrete detaljer om authentication og authorization flow.',
confidence: 0.75,
recommendations: [
'Implementér OAuth 2.0 med PKCE for frontend authentication',
'Brug JWT med kort levetid og refresh tokens',
'Tilføj rate limiting på alle API endpoints'
],
concerns: [
'Ingen beskrivelse af encryption at rest',
'Manglende detaljer om GDPR compliance',
'Uklart hvordan sensitiv data håndteres i logs'
]
},
backend: {
persona: 'backend',
role: 'Backend Expert',
feedback: 'API-designet følger RESTful principper godt. Performance-overvejelser skal dog specificeres mere detaljeret.',
confidence: 0.88,
recommendations: [
'Implementér database connection pooling',
'Overvej GraphQL for komplekse data-queries',
'Tilføj API versioning fra start'
],
concerns: [
'Manglende beskrivelse af database indexing strategi',
'Uklart hvordan N+1 query problemet undgås'
]
},
frontend: {
persona: 'frontend',
role: 'Frontend Expert',
feedback: 'UI/UX-beskrivelsen er god, men der mangler detaljer om accessibility og responsive design implementering.',
confidence: 0.79,
recommendations: [
'Implementér WCAG 2.1 Level AA standarder',
'Brug progressive enhancement for bedre compatibility',
'Tilføj offline-first capabilities med service workers'
],
concerns: [
'Ingen beskrivelse af keyboard navigation',
'Manglende plan for internationalization (i18n)',
'Uklart hvordan fejl præsenteres for brugeren'
]
}
};
// Build response based on selected personas
const personas: PersonaFeedback[] = selectedPersonas
.filter(p => personaFeedbackMap[p])
.map(p => personaFeedbackMap[p]);
// Find consensus and disagreements
const allRecommendations = personas.flatMap(p => p.recommendations);
const allConcerns = personas.flatMap(p => p.concerns);
const consensus = [
'Systemet har et solidt fundament med god separation of concerns',
'Der er behov for mere detaljering omkring fejlhåndtering og resiliens',
'Sikkerhed og performance skal prioriteres højere i implementeringsfasen'
];
const disagreements = [
'Architecture anbefaler event-driven, mens Backend foretrækker traditionel REST',
'Security ønsker streng authentication, Frontend ønsker friktionsløs brugeroplevelse',
'Trade-off mellem kompleksitet og fleksibilitet i arkitekturen'
];
// Calculate overall score based on confidence
const avgConfidence = personas.reduce((sum, p) => sum + p.confidence, 0) / personas.length;
const overallScore = Math.round(avgConfidence * 100);
const result: ReviewResult = {
summary: `${personas.length} eksperter har gennemgået specifikationen. Overordnet vurdering: God struktur med behov for flere tekniske detaljer.`,
consensus,
disagreements,
personas,
overallScore
};
res.json(result);
} catch (error) {
console.error('Spec panel review error:', error);
res.status(500).json({
error: 'Review failed',
message: error instanceof Error ? error.message : 'Unknown error occurred'
});
}
});
// Health check endpoint
router.get('/health', (_req: Request, res: Response) => {
res.json({
status: 'ok',
service: 'sc',
endpoints: ['/analyze', '/spec-panel'],
timestamp: new Date().toISOString()
});
});
export { router as scRouter };