File size: 6,609 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
236
237
238
import { NextRequest, NextResponse } from "next/server";
import ZAI from "z-ai-web-dev-sdk";
import { db } from "@/lib/db";
import fs from "fs/promises";
import path from "path";

const DOWNLOAD_DIR = path.join(process.cwd(), "download", "images");

// Asegurar directorio existe
async function ensureDir() {
  try {
    await fs.mkdir(DOWNLOAD_DIR, { recursive: true });
  } catch {
    // ya existe
  }
}

// Reglas de censura por plataforma
const CENSOR_RULES: Record<string, string[]> = {
  youtube: [
    "no nudity",
    "no sexual content",
    "no graphic violence",
    "family friendly"
  ],
  tiktok: [
    "no nudity",
    "no sexual suggestiveness",
    "no graphic violence",
    "no self-harm content",
    "age appropriate"
  ],
  instagram: [
    "no nudity",
    "no graphic violence",
    "artistic content acceptable with moderation",
    "no self-harm imagery"
  ],
  twitter: [
    "content warning if sensitive",
    "no illegal content"
  ],
  general: [
    "safe for work",
    "no explicit content"
  ]
};

// Aplicar filtros de censura al prompt
function applyCensorFilter(prompt: string, platform: string): string {
  const rules = CENSOR_RULES[platform] || CENSOR_RULES.general;
  const censorAdditions = rules.map(rule => `(${rule})`).join(", ");
  return `${prompt}, ${censorAdditions}`;
}

export async function POST(request: NextRequest) {
  try {
    const body = await request.json();
    const { 
      prompt, 
      optimizedPrompt,
      platform = "general",
      character,
      style = "realistic",
      size = "1024x1024",
      saveToDb = true,
      projectId
    } = body;

    if (!prompt && !optimizedPrompt) {
      return NextResponse.json(
        { success: false, error: "Se requiere un prompt" },
        { status: 400 }
      );
    }

    await ensureDir();

    // Usar el prompt optimizado si está disponible
    let finalPrompt = optimizedPrompt || prompt;

    // Aplicar filtros de censura según plataforma
    finalPrompt = applyCensorFilter(finalPrompt, platform);

    // Añadir estilo si se especifica
    if (style && style !== "realistic") {
      const styleAdditions: Record<string, string> = {
        anime: "anime style, manga aesthetic, vibrant colors, cel shading",
        realistic: "photorealistic, highly detailed, 8K, professional photography",
        artistic: "digital art, artistic style, creative interpretation, masterpiece",
        "oil-painting": "oil painting style, classical art, brush strokes, museum quality",
        sketch: "pencil sketch, hand drawn, artistic lines, detailed shading",
        "3d": "3D render, octane render, cinema 4D, volumetric lighting"
      };
      if (styleAdditions[style]) {
        finalPrompt = `${finalPrompt}, ${styleAdditions[style]}`;
      }
    }

    // Añadir referencia de personaje si existe
    if (character) {
      finalPrompt = `${finalPrompt}, character: ${character}`;
    }

    console.log("🖼️ Generando imagen con prompt:", finalPrompt.substring(0, 100) + "...");

    // Crear registro en BD si se solicita
    let contentRecord = null;
    if (saveToDb) {
      contentRecord = await db.content.create({
        data: {
          type: "image",
          title: prompt.substring(0, 50),
          description: prompt,
          prompt: prompt,
          optimizedPrompt: finalPrompt,
          platform: platform,
          status: "processing",
          projectId: projectId || null,
        }
      });
    }

    try {
      // Generar imagen con z-ai-web-dev-sdk
      const zai = await ZAI.create();
      const response = await zai.images.generations.create({
        prompt: finalPrompt,
        size: size as "1024x1024" | "768x1344" | "864x1152" | "1344x768" | "1152x864" | "1440x720" | "720x1440"
      });

      const imageBase64 = response.data[0]?.base64;

      if (!imageBase64) {
        throw new Error("No se recibió imagen del generador");
      }

      // Guardar imagen en disco
      const filename = `image_${Date.now()}.png`;
      const filepath = path.join(DOWNLOAD_DIR, filename);
      const buffer = Buffer.from(imageBase64, "base64");
      await fs.writeFile(filepath, buffer);

      // Actualizar registro en BD
      if (contentRecord) {
        await db.content.update({
          where: { id: contentRecord.id },
          data: {
            status: "completed",
            filePath: `/download/images/${filename}`,
            thumbnail: `/download/images/${filename}`,
          }
        });
      }

      // Crear tarea de agente
      await db.agentTask.create({
        data: {
          type: "generate_image",
          status: "completed",
          input: prompt,
          output: `Imagen generada: ${filename}`,
          completedAt: new Date(),
        }
      });

      return NextResponse.json({
        success: true,
        image: {
          id: contentRecord?.id,
          filename,
          url: `/download/images/${filename}`,
          base64: imageBase64.substring(0, 50) + "...", // Preview del base64
          prompt: finalPrompt,
          platform,
          size
        }
      });

    } catch (genError) {
      console.error("Error generando imagen:", genError);
      
      if (contentRecord) {
        await db.content.update({
          where: { id: contentRecord.id },
          data: {
            status: "failed",
            metadata: JSON.stringify({ error: String(genError) })
          }
        });
      }

      return NextResponse.json(
        { success: false, error: "Error al generar imagen: " + String(genError) },
        { status: 500 }
      );
    }

  } catch (error) {
    console.error("Error en generate image:", error);
    return NextResponse.json(
      { success: false, error: "Error interno del servidor" },
      { status: 500 }
    );
  }
}

// Listar imágenes generadas
export async function GET(request: NextRequest) {
  try {
    const { searchParams } = new URL(request.url);
    const platform = searchParams.get("platform");
    const limit = parseInt(searchParams.get("limit") || "20");

    const where: Record<string, unknown> = { type: "image" };
    if (platform) where.platform = platform;

    const images = await db.content.findMany({
      where,
      orderBy: { createdAt: "desc" },
      take: limit,
    });

    return NextResponse.json({
      success: true,
      images,
      total: images.length
    });

  } catch (error) {
    console.error("Error listing images:", error);
    return NextResponse.json(
      { success: false, error: "Error al listar imágenes" },
      { status: 500 }
    );
  }
}