Spaces:
Paused
Paused
File size: 10,736 Bytes
529090e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 | 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 };
|