File size: 6,632 Bytes
333c51a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
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 }
    );
  }
}