GoGma commited on
Commit
625d2ed
·
verified ·
1 Parent(s): a9c09de

Update src/app/api/generate/image/route.ts

Browse files
Files changed (1) hide show
  1. src/app/api/generate/image/route.ts +196 -49
src/app/api/generate/image/route.ts CHANGED
@@ -1,86 +1,233 @@
1
  import { NextRequest, NextResponse } from "next/server";
2
  import ZAI from "z-ai-web-dev-sdk";
3
  import { db } from "@/lib/db";
 
4
 
5
- const CENSOR_RULES: Record<string, string> = {
6
- youtube: "family friendly, no nudity, no violence",
7
- tiktok: "age appropriate, no suggestive content",
8
- instagram: "community guidelines, no graphic content",
9
- twitter: "content warning if sensitive",
10
- general: "safe for work"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  };
12
 
 
 
 
 
 
 
 
13
  export async function POST(request: NextRequest) {
14
  try {
15
  const body = await request.json();
16
- const { prompt, optimizedPrompt, platform = "general", style = "realistic", size = "1024x1024" } = body;
17
 
18
- if (!prompt && !optimizedPrompt) {
19
- return NextResponse.json({ success: false, error: "Se requiere un prompt" }, { status: 400 });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  }
21
 
22
- let finalPrompt = optimizedPrompt || prompt;
23
- const censorRule = CENSOR_RULES[platform] || CENSOR_RULES.general;
24
- finalPrompt = finalPrompt + ", " + censorRule;
25
-
26
- const styleMap: Record<string, string> = {
27
- anime: "anime style, manga aesthetic",
28
- realistic: "photorealistic, highly detailed",
29
- artistic: "digital art, creative"
30
- };
31
- if (styleMap[style]) finalPrompt = finalPrompt + ", " + styleMap[style];
32
-
33
- const contentRecord = await db.content.create({
34
- data: {
35
- type: "image",
36
- title: prompt.substring(0, 50),
37
- description: prompt,
38
- prompt: prompt,
39
- optimizedPrompt: finalPrompt,
40
- platform: platform,
41
- status: "processing"
42
  }
43
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
 
45
  try {
46
  const zai = await ZAI.create();
 
47
  const response = await zai.images.generations.create({
48
  prompt: finalPrompt,
49
- size: size as "1024x1024"
 
 
 
 
 
 
 
50
  });
51
 
52
- const imageBase64 = response.data[0]?.base64;
53
- if (!imageBase64) throw new Error("No se recibio imagen");
54
 
55
- await db.content.update({
56
- where: { id: contentRecord.id },
57
- data: { status: "completed", filePath: "generated" }
58
- });
59
 
60
- await db.agentTask.create({
61
- data: { type: "generate_image", status: "completed", input: prompt, output: "Imagen generada", completedAt: new Date() }
62
- });
63
 
64
- return NextResponse.json({ success: true, image: { id: contentRecord.id, base64: imageBase64, prompt: finalPrompt, platform, size } });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  } catch (genError) {
66
- await db.content.update({ where: { id: contentRecord.id }, data: { status: "failed" } });
67
- return NextResponse.json({ success: false, error: String(genError) }, { status: 500 });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  }
69
  } catch (error) {
70
- return NextResponse.json({ success: false, error: "Error interno" }, { status: 500 });
 
 
 
 
71
  }
72
  }
73
 
 
74
  export async function GET(request: NextRequest) {
75
  try {
76
  const { searchParams } = new URL(request.url);
77
  const platform = searchParams.get("platform");
78
  const limit = parseInt(searchParams.get("limit") || "20");
 
79
  const where: Record<string, unknown> = { type: "image" };
80
  if (platform) where.platform = platform;
81
- const images = await db.content.findMany({ where, orderBy: { createdAt: "desc" }, take: limit });
82
- return NextResponse.json({ success: true, images, total: images.length });
83
- } catch {
84
- return NextResponse.json({ success: false, error: "Error" }, { status: 500 });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  }
86
- }
 
1
  import { NextRequest, NextResponse } from "next/server";
2
  import ZAI from "z-ai-web-dev-sdk";
3
  import { db } from "@/lib/db";
4
+ import type { Content } from "@prisma/client";
5
 
6
+ // Reglas de censura por plataforma
7
+ const CENSOR_RULES: Record<string, string[]> = {
8
+ youtube: [
9
+ "no nudity",
10
+ "no sexual content",
11
+ "no graphic violence",
12
+ "family friendly"
13
+ ],
14
+ tiktok: [
15
+ "no nudity",
16
+ "no sexual suggestiveness",
17
+ "no graphic violence",
18
+ "no self-harm content",
19
+ "age appropriate"
20
+ ],
21
+ instagram: [
22
+ "no nudity",
23
+ "no graphic violence",
24
+ "artistic content acceptable with moderation",
25
+ "no self-harm imagery"
26
+ ],
27
+ twitter: [
28
+ "content warning if sensitive",
29
+ "no illegal content"
30
+ ],
31
+ general: [
32
+ "safe for work",
33
+ "no explicit content"
34
+ ]
35
  };
36
 
37
+ // Aplicar filtros de censura al prompt
38
+ function applyCensorFilter(prompt: string, platform: string): string {
39
+ const rules = CENSOR_RULES[platform] || CENSOR_RULES.general;
40
+ const censorAdditions = rules.map(rule => `(${rule})`).join(", ");
41
+ return `${prompt}, ${censorAdditions}`;
42
+ }
43
+
44
  export async function POST(request: NextRequest) {
45
  try {
46
  const body = await request.json();
 
47
 
48
+ const {
49
+ prompt,
50
+ optimizedPrompt,
51
+ platform = "general",
52
+ character,
53
+ style = "realistic",
54
+ size = "1024x1024",
55
+ saveToDb = true,
56
+ projectId
57
+ } = body;
58
+
59
+ const basePrompt: string | undefined = optimizedPrompt || prompt;
60
+
61
+ if (!basePrompt) {
62
+ return NextResponse.json(
63
+ { success: false, error: "Se requiere un prompt" },
64
+ { status: 400 }
65
+ );
66
  }
67
 
68
+ let finalPrompt = applyCensorFilter(basePrompt, platform);
69
+
70
+ // Estilos
71
+ if (style && style !== "realistic") {
72
+ const styleAdditions: Record<string, string> = {
73
+ anime: "anime style, manga aesthetic, vibrant colors, cel shading",
74
+ realistic: "photorealistic, highly detailed, 8K, professional photography",
75
+ artistic: "digital art, artistic style, creative interpretation, masterpiece",
76
+ "oil-painting": "oil painting style, classical art, brush strokes, museum quality",
77
+ sketch: "pencil sketch, hand drawn, artistic lines, detailed shading",
78
+ "3d": "3D render, octane render, cinema 4D, volumetric lighting"
79
+ };
80
+
81
+ if (styleAdditions[style]) {
82
+ finalPrompt = `${finalPrompt}, ${styleAdditions[style]}`;
 
 
 
 
 
83
  }
84
+ }
85
+
86
+ // Personaje
87
+ if (character) {
88
+ finalPrompt = `${finalPrompt}, character: ${character}`;
89
+ }
90
+
91
+ console.log(
92
+ "🖼️ Generando imagen:",
93
+ finalPrompt.substring(0, 100) + "..."
94
+ );
95
+
96
+ // ✅ Crear registro en BD
97
+ let contentRecord: Content | null = null;
98
+
99
+ if (saveToDb) {
100
+ contentRecord = await db.content.create({
101
+ data: {
102
+ type: "image",
103
+ title: basePrompt.substring(0, 50),
104
+ description: basePrompt,
105
+ prompt: basePrompt,
106
+ optimizedPrompt: finalPrompt,
107
+ platform,
108
+ status: "processing",
109
+ projectId: projectId || null
110
+ }
111
+ });
112
+ }
113
 
114
  try {
115
  const zai = await ZAI.create();
116
+
117
  const response = await zai.images.generations.create({
118
  prompt: finalPrompt,
119
+ size: size as
120
+ | "1024x1024"
121
+ | "768x1344"
122
+ | "864x1152"
123
+ | "1344x768"
124
+ | "1152x864"
125
+ | "1440x720"
126
+ | "720x1440"
127
  });
128
 
129
+ const imageBase64 = (response as any)?.data?.[0]?.base64;
 
130
 
131
+ if (!imageBase64) {
132
+ throw new Error("No se recibió imagen del generador");
133
+ }
 
134
 
135
+ const filename = `image_${Date.now()}.png`;
136
+ const imageUrl = `/download/images/${filename}`;
 
137
 
138
+ // actualizar BD
139
+ if (contentRecord) {
140
+ await db.content.update({
141
+ where: { id: contentRecord.id },
142
+ data: {
143
+ status: "completed",
144
+ filePath: imageUrl,
145
+ thumbnail: imageUrl,
146
+ metadata: JSON.stringify({ base64: imageBase64 })
147
+ }
148
+ });
149
+ }
150
+
151
+ // ✅ tarea agente (no romper si falla)
152
+ try {
153
+ await db.agentTask.create({
154
+ data: {
155
+ type: "generate_image",
156
+ status: "completed",
157
+ input: basePrompt,
158
+ output: `Imagen generada: ${filename}`,
159
+ completedAt: new Date()
160
+ }
161
+ });
162
+ } catch (e) {
163
+ console.warn("agentTask warning:", e);
164
+ }
165
+
166
+ return NextResponse.json({
167
+ success: true,
168
+ image: {
169
+ id: contentRecord?.id ?? null,
170
+ filename,
171
+ url: imageUrl,
172
+ base64: imageBase64,
173
+ prompt: finalPrompt,
174
+ platform,
175
+ size
176
+ }
177
+ });
178
  } catch (genError) {
179
+ console.error("Error generando imagen:", genError);
180
+
181
+ if (contentRecord) {
182
+ await db.content.update({
183
+ where: { id: contentRecord.id },
184
+ data: {
185
+ status: "failed",
186
+ metadata: JSON.stringify({ error: String(genError) })
187
+ }
188
+ });
189
+ }
190
+
191
+ return NextResponse.json(
192
+ { success: false, error: "Error al generar imagen: " + String(genError) },
193
+ { status: 500 }
194
+ );
195
  }
196
  } catch (error) {
197
+ console.error("Error en generate image:", error);
198
+ return NextResponse.json(
199
+ { success: false, error: "Error interno del servidor" },
200
+ { status: 500 }
201
+ );
202
  }
203
  }
204
 
205
+ // ✅ LISTAR IMÁGENES
206
  export async function GET(request: NextRequest) {
207
  try {
208
  const { searchParams } = new URL(request.url);
209
  const platform = searchParams.get("platform");
210
  const limit = parseInt(searchParams.get("limit") || "20");
211
+
212
  const where: Record<string, unknown> = { type: "image" };
213
  if (platform) where.platform = platform;
214
+
215
+ const images = await db.content.findMany({
216
+ where,
217
+ orderBy: { createdAt: "desc" },
218
+ take: limit
219
+ });
220
+
221
+ return NextResponse.json({
222
+ success: true,
223
+ images,
224
+ total: images.length
225
+ });
226
+ } catch (error) {
227
+ console.error("Error listing images:", error);
228
+ return NextResponse.json(
229
+ { success: false, error: "Error al listar imágenes" },
230
+ { status: 500 }
231
+ );
232
  }
233
+ }