sofia-cloud / src /app /api /analyze /route.ts
Gmagl
Add Sofia Cloud complete files
333c51a
Raw
History Blame
6.63 kB
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<string> {
const repoPath = path.join(REPOS_DIR, repoName);
let content = "";
let fileCount = 0;
async function readDir(dir: string, depth: number = 0): Promise<void> {
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<string, string> = {
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<string, string> = {};
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 }
);
}
}