import { NextRequest, NextResponse } from "next/server"; import { db } from "@/lib/db"; import ZAI from "z-ai-web-dev-sdk"; import fs from "fs/promises"; import path from "path"; const REPOS_DIR = path.join(process.cwd(), "repos"); async function readRepoFiles(repoName: string, maxFiles: number = 10): Promise { const repoPath = path.join(REPOS_DIR, repoName); let content = ""; let fileCount = 0; async function readDir(dir: string, depth: number = 0): Promise { if (fileCount >= maxFiles || depth > 3) return; try { const entries = await fs.readdir(dir, { withFileTypes: true }); for (const entry of entries) { if (fileCount >= maxFiles) break; const fullPath = path.join(dir, entry.name); // Ignorar carpetas comunes if (entry.isDirectory()) { if (["node_modules", ".git", "dist", "build", "__pycache__", "venv", ".next"].includes(entry.name)) { continue; } await readDir(fullPath, depth + 1); } else if (entry.isFile()) { // Solo archivos de código relevantes const ext = path.extname(entry.name); const codeExtensions = [".ts", ".tsx", ".js", ".jsx", ".py", ".java", ".go", ".rs", ".c", ".cpp", ".h", ".css", ".scss", ".html", ".json", ".yaml", ".yml", ".md"]; if (codeExtensions.includes(ext) && !entry.name.startsWith(".")) { try { const fileContent = await fs.readFile(fullPath, "utf-8"); const relativePath = path.relative(repoPath, fullPath); content += `\n--- ${relativePath} ---\n${fileContent.slice(0, 2000)}\n`; fileCount++; } catch { // Archivo no legible } } } } } catch { // Directorio no accesible } } await readDir(repoPath); return content; } // POST - Analizar código con IA export async function POST(request: NextRequest) { try { const body = await request.json(); const { repoId, projectId, type, code } = body; let analysisContent = code || ""; let repoName = ""; // Si hay repoId, leer archivos del repositorio if (repoId) { const repo = await db.repo.findUnique({ where: { id: repoId }, }); if (repo) { repoName = repo.name; analysisContent = await readRepoFiles(repo.name); } } if (!analysisContent) { return NextResponse.json( { success: false, error: "No hay código para analizar" }, { status: 400 } ); } const analysisType = type || "code"; // Crear análisis inicial const analysis = await db.analysis.create({ data: { type: analysisType, result: "Analizando...", repoId: repoId || null, projectId: projectId || null, }, }); try { const zai = await ZAI.create(); const prompts: Record = { code: `Analiza el siguiente código y proporciona: 1. Resumen general del proyecto 2. Estructura y organización 3. Calidad del código (0-10) 4. Posibles mejoras 5. Bugs o problemas potenciales 6. Sugerencias de seguridad Código a analizar: ${analysisContent.slice(0, 10000)}`, security: `Realiza un análisis de seguridad del siguiente código. Identifica: 1. Vulnerabilidades potenciales (SQL injection, XSS, etc.) 2. Exposición de datos sensibles 3. Dependencias inseguras 4. Configuraciones peligrosas 5. Recomendaciones de mitigación Código: ${analysisContent.slice(0, 10000)}`, performance: `Analiza el rendimiento del siguiente código: 1. Cuellos de botella potenciales 2. Complejidad algorítmica 3. Uso de memoria 4. Operaciones bloqueantes 5. Optimizaciones sugeridas Código: ${analysisContent.slice(0, 10000)}`, }; const completion = await zai.chat.completions.create({ messages: [ { role: "system", content: "Eres un experto en análisis de código. Proporciona análisis detallados y accionables.", }, { role: "user", content: prompts[analysisType] || prompts.code, }, ], temperature: 0.3, max_tokens: 4000, }); const result = completion.choices[0]?.message?.content || "No se pudo generar análisis"; // Extraer resumen (primeras 200 caracteres) const summary = result.slice(0, 200) + "..."; // Actualizar análisis const updatedAnalysis = await db.analysis.update({ where: { id: analysis.id }, data: { result, summary, }, }); // Crear tarea de agente await db.agentTask.create({ data: { type: "analyze", status: "completed", input: `Analizar ${repoName || "código"} (${analysisType})`, output: summary, completedAt: new Date(), }, }); return NextResponse.json({ success: true, analysis: updatedAnalysis, message: "Análisis completado", }); } catch (aiError) { console.error("AI Analysis Error:", aiError); await db.analysis.update({ where: { id: analysis.id }, data: { result: "Error en el análisis: " + (aiError instanceof Error ? aiError.message : "Error desconocido"), }, }); return NextResponse.json( { success: false, error: "Error en el análisis de IA" }, { status: 500 } ); } } catch (error) { console.error("Error in analyze route:", error); return NextResponse.json( { success: false, error: "Error interno del servidor" }, { status: 500 } ); } } // GET - Obtener análisis export async function GET(request: NextRequest) { try { const { searchParams } = new URL(request.url); const repoId = searchParams.get("repoId"); const projectId = searchParams.get("projectId"); const where: Record = {}; if (repoId) where.repoId = repoId; if (projectId) where.projectId = projectId; const analyses = await db.analysis.findMany({ where: Object.keys(where).length > 0 ? where : undefined, include: { repo: true, project: true, }, orderBy: { createdAt: "desc" }, take: 20, }); return NextResponse.json({ success: true, analyses, }); } catch (error) { console.error("Error fetching analyses:", error); return NextResponse.json( { success: false, error: "Error al obtener análisis" }, { status: 500 } ); } }