Gmagl commited on
Commit
30ad7ef
·
1 Parent(s): acbcb20

Fix: All API routes corrected - no fs/path, proper typing

Browse files
src/app/api/characters/route.ts CHANGED
@@ -1,212 +1,86 @@
1
  import { NextRequest, NextResponse } from "next/server";
2
  import { db } from "@/lib/db";
3
  import ZAI from "z-ai-web-dev-sdk";
4
- import fs from "fs/promises";
5
- import path from "path";
6
 
7
- const CHARACTERS_DIR = path.join(process.cwd(), "download", "characters");
8
-
9
- async function ensureDir() {
10
- try {
11
- await fs.mkdir(CHARACTERS_DIR, { recursive: true });
12
- } catch {
13
- // ya existe
14
- }
15
- }
16
-
17
- // GET - Listar personajes
18
  export async function GET(request: NextRequest) {
19
  try {
20
- const characters = await db.character.findMany({
21
- include: {
22
- contents: {
23
- take: 5,
24
- orderBy: { createdAt: "desc" }
25
- }
26
- },
27
- orderBy: { createdAt: "desc" }
28
- });
29
-
30
- return NextResponse.json({
31
- success: true,
32
- characters,
33
- total: characters.length
34
- });
35
-
36
- } catch (error) {
37
- console.error("Error fetching characters:", error);
38
- return NextResponse.json(
39
- { success: false, error: "Error al obtener personajes" },
40
- { status: 500 }
41
- );
42
  }
43
  }
44
 
45
- // POST - Crear personaje
46
  export async function POST(request: NextRequest) {
47
  try {
48
  const body = await request.json();
49
- const {
50
- name,
51
- description,
52
- generateReference = false,
53
- traits
54
- } = body;
55
 
56
  if (!name) {
57
- return NextResponse.json(
58
- { success: false, error: "El nombre del personaje es requerido" },
59
- { status: 400 }
60
- );
61
  }
62
 
63
- await ensureDir();
64
-
65
- let referenceImage = null;
66
- let characterTraits = traits;
67
 
68
- // Generar imagen de referencia si se solicita
69
  if (generateReference) {
70
  try {
71
  const zai = await ZAI.create();
72
-
73
- // Crear prompt detallado para el personaje
74
- const characterPrompt = `Character reference sheet: ${name}. ${description || "Original character"}. Full body view, multiple angles (front, side, back), consistent character design, clear details, neutral pose, white background, character turnaround, reference sheet style. Professional character design, consistent facial features, consistent body proportions.`;
75
-
76
  const response = await zai.images.generations.create({
77
- prompt: characterPrompt,
78
- size: "1344x768" // Horizontal para el reference sheet
79
  });
80
-
81
  const imageBase64 = response.data[0]?.base64;
82
  if (imageBase64) {
83
- const filename = `character_${name.toLowerCase().replace(/\s+/g, "_")}_${Date.now()}.png`;
84
- const filepath = path.join(CHARACTERS_DIR, filename);
85
- const buffer = Buffer.from(imageBase64, "base64");
86
- await fs.writeFile(filepath, buffer);
87
- referenceImage = `/download/characters/${filename}`;
88
- }
89
-
90
- // Si no se proporcionaron traits, extraerlos con IA
91
- if (!traits) {
92
- const traitsResponse = await zai.chat.completions.create({
93
- messages: [
94
- {
95
- role: "system",
96
- content: "Eres un experto en diseño de personajes. Extrae los rasgos físicos clave del personaje para mantener consistencia en futuras generaciones. Responde SOLO en formato JSON con: { \"face\": \"\", \"body\": \"\", \"hair\": \"\", \"clothing\": \"\", \"colors\": \"\", \"distinctive\": \"\" }"
97
- },
98
- {
99
- role: "user",
100
- content: `Personaje: ${name}. Descripción: ${description || "Original character"}`
101
- }
102
- ]
103
- });
104
-
105
- const traitsText = traitsResponse.choices[0]?.message?.content || "";
106
- try {
107
- const jsonMatch = traitsText.match(/\{[\s\S]*\}/);
108
- if (jsonMatch) {
109
- characterTraits = jsonMatch[0];
110
- }
111
- } catch {
112
- characterTraits = JSON.stringify({ description: description || "Original character" });
113
  }
114
  }
115
-
116
- } catch (genError) {
117
- console.error("Error generando referencia:", genError);
118
- // Continuar sin imagen de referencia
119
- }
120
  }
121
 
122
- // Crear personaje en BD
123
- const character = await db.character.create({
124
- data: {
125
- name,
126
- description: description || null,
127
- referenceImage,
128
- traits: characterTraits || JSON.stringify({ name, description })
129
- }
130
- });
131
 
132
- return NextResponse.json({
133
- success: true,
134
- character,
135
- message: `Personaje "${name}" creado exitosamente`
136
  });
137
 
138
- } catch (error) {
139
- console.error("Error creating character:", error);
140
- return NextResponse.json(
141
- { success: false, error: "Error al crear personaje" },
142
- { status: 500 }
143
- );
144
  }
145
  }
146
 
147
- // PUT - Actualizar personaje
148
  export async function PUT(request: NextRequest) {
149
  try {
150
  const body = await request.json();
151
  const { id, name, description, traits } = body;
 
152
 
153
- if (!id) {
154
- return NextResponse.json(
155
- { success: false, error: "ID del personaje requerido" },
156
- { status: 400 }
157
- );
158
- }
159
-
160
- const character = await db.character.update({
161
- where: { id },
162
- data: {
163
- name: name || undefined,
164
- description: description || undefined,
165
- traits: traits || undefined,
166
- }
167
- });
168
-
169
- return NextResponse.json({
170
- success: true,
171
- character
172
- });
173
 
174
- } catch (error) {
175
- console.error("Error updating character:", error);
176
- return NextResponse.json(
177
- { success: false, error: "Error al actualizar personaje" },
178
- { status: 500 }
179
- );
180
  }
181
  }
182
 
183
- // DELETE - Eliminar personaje
184
  export async function DELETE(request: NextRequest) {
185
  try {
186
  const { searchParams } = new URL(request.url);
187
  const id = searchParams.get("id");
 
188
 
189
- if (!id) {
190
- return NextResponse.json(
191
- { success: false, error: "ID requerido" },
192
- { status: 400 }
193
- );
194
- }
195
-
196
- await db.character.delete({
197
- where: { id }
198
- });
199
-
200
- return NextResponse.json({
201
- success: true,
202
- message: "Personaje eliminado"
203
- });
204
-
205
- } catch (error) {
206
- console.error("Error deleting character:", error);
207
- return NextResponse.json(
208
- { success: false, error: "Error al eliminar personaje" },
209
- { status: 500 }
210
- );
211
  }
212
- }
 
1
  import { NextRequest, NextResponse } from "next/server";
2
  import { db } from "@/lib/db";
3
  import ZAI from "z-ai-web-dev-sdk";
 
 
4
 
 
 
 
 
 
 
 
 
 
 
 
5
  export async function GET(request: NextRequest) {
6
  try {
7
+ const characters = await db.character.findMany({ orderBy: { createdAt: "desc" } });
8
+ return NextResponse.json({ success: true, characters, total: characters.length });
9
+ } catch {
10
+ return NextResponse.json({ success: false, error: "Error" }, { status: 500 });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  }
12
  }
13
 
 
14
  export async function POST(request: NextRequest) {
15
  try {
16
  const body = await request.json();
17
+ const { name, description, generateReference, traits } = body;
 
 
 
 
 
18
 
19
  if (!name) {
20
+ return NextResponse.json({ success: false, error: "Nombre requerido" }, { status: 400 });
 
 
 
21
  }
22
 
23
+ let referenceImage: string | null = null;
24
+ let characterTraits: string | null = null;
 
 
25
 
 
26
  if (generateReference) {
27
  try {
28
  const zai = await ZAI.create();
 
 
 
 
29
  const response = await zai.images.generations.create({
30
+ prompt: "Character reference: " + name + ", " + (description || "original character"),
31
+ size: "1024x1024"
32
  });
 
33
  const imageBase64 = response.data[0]?.base64;
34
  if (imageBase64) {
35
+ referenceImage = "generated_" + Date.now();
36
+ if (!traits) {
37
+ characterTraits = JSON.stringify({ name, description });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  }
39
  }
40
+ } catch {}
 
 
 
 
41
  }
42
 
43
+ const finalTraits = traits || characterTraits || JSON.stringify({ name });
 
 
 
 
 
 
 
 
44
 
45
+ const character = await db.character.create({
46
+ data: { name, description: description || null, referenceImage, traits: finalTraits }
 
 
47
  });
48
 
49
+ return NextResponse.json({ success: true, character });
50
+ } catch {
51
+ return NextResponse.json({ success: false, error: "Error" }, { status: 500 });
 
 
 
52
  }
53
  }
54
 
 
55
  export async function PUT(request: NextRequest) {
56
  try {
57
  const body = await request.json();
58
  const { id, name, description, traits } = body;
59
+ if (!id) return NextResponse.json({ success: false, error: "ID requerido" }, { status: 400 });
60
 
61
+ const data: { name?: string; description?: string | null; traits?: string } = {};
62
+ if (name) data.name = name;
63
+ if (description !== undefined) data.description = description || null;
64
+ if (traits) data.traits = traits;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
 
66
+ const character = await db.character.update({ where: { id }, data });
67
+ return NextResponse.json({ success: true, character });
68
+ } catch {
69
+ return NextResponse.json({ success: false, error: "Error" }, { status: 500 });
 
 
70
  }
71
  }
72
 
 
73
  export async function DELETE(request: NextRequest) {
74
  try {
75
  const { searchParams } = new URL(request.url);
76
  const id = searchParams.get("id");
77
+ if (!id) return NextResponse.json({ success: false, error: "ID requerido" }, { status: 400 });
78
 
79
+ await db.pet.deleteMany({ where: { characterId: id } });
80
+ await db.content.updateMany({ where: { characterId: id }, data: { characterId: null } });
81
+ await db.character.delete({ where: { id } });
82
+ return NextResponse.json({ success: true });
83
+ } catch {
84
+ return NextResponse.json({ success: false, error: "Error" }, { status: 500 });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  }
86
+ }
src/app/api/influencers/route.ts CHANGED
@@ -1,450 +1,70 @@
1
  import { NextRequest, NextResponse } from "next/server";
2
  import ZAI from "z-ai-web-dev-sdk";
3
- import { prisma } from "@/lib/db";
4
 
5
- // Base de datos de influencers IA famosos (para análisis y referencia)
6
- const FAMOUS_AI_INFLUENCERS = [
7
- {
8
- name: "Lil Miquela",
9
- handle: "@lilmiquela",
10
- platform: "Instagram",
11
- followers: 2500000,
12
- engagement: 3.2,
13
- niche: "Fashion & Lifestyle",
14
- style: {
15
- aesthetic: "Hyperrealistic CGI",
16
- tone: "Gen Z influencer",
17
- themes: ["fashion", "music", "social issues"]
18
- },
19
- contentTypes: ["photos", "videos", "stories"],
20
- postingSchedule: { frequency: "3-4 per week", bestTimes: ["12:00", "18:00", "21:00"] },
21
- visualStyle: { colors: ["warm", "urban"], lighting: "golden hour", editing: "cinematic" },
22
- monetizationType: ["brand deals", "merchandise"],
23
- signatureElements: ["freckles", "bangs", "streetwear"],
24
- petCompanion: false,
25
- lessons: [
26
- "Consistencia visual es clave",
27
- "Narrativa de vida ficticia pero creíble",
28
- "Colaboraciones con marcas reales",
29
- "Activismo social para conectar con audiencia"
30
- ]
31
- },
32
- {
33
- name: "Rozy",
34
- handle: "@rozy.gram",
35
- platform: "Instagram",
36
- followers: 180000,
37
- engagement: 4.5,
38
- niche: "Fashion & K-pop style",
39
- style: {
40
- aesthetic: "Korean beauty standard",
41
- tone: "Friendly and trendy",
42
- themes: ["fashion", "beauty", "travel"]
43
- },
44
- contentTypes: ["photos", "reels", "stories"],
45
- postingSchedule: { frequency: "5-7 per week", bestTimes: ["11:00", "19:00"] },
46
- visualStyle: { colors: ["pastel", "bright"], lighting: "soft", editing: "k-beauty" },
47
- monetizationType: ["brand ambassador", "product placement"],
48
- signatureElements: ["k-pop fashion", "dance videos", "korean makeup"],
49
- petCompanion: true,
50
- petType: "small dog",
51
- lessons: [
52
- "Aprovechar tendencias K-pop",
53
- "Contenido de baile viral",
54
- "Estética cuidada y consistente",
55
- "Mascota como elemento diferenciador"
56
- ]
57
- },
58
- {
59
- name: "Imma",
60
- handle: "@imma.gram",
61
- platform: "Instagram",
62
- followers: 390000,
63
- engagement: 3.8,
64
- niche: "Fashion & Art",
65
- style: {
66
- aesthetic: "Japanese street style",
67
- tone: "Artistic and edgy",
68
- themes: ["fashion", "art", "technology"]
69
- },
70
- contentTypes: ["photos", "reels", "collaborations"],
71
- postingSchedule: { frequency: "4-5 per week", bestTimes: ["10:00", "20:00"] },
72
- visualStyle: { colors: ["neon accents", "monochrome"], lighting: "dramatic", editing: "artistic" },
73
- monetizationType: ["brand collaborations", "NFTs", "art exhibitions"],
74
- signatureElements: ["pink bob hair", "futuristic fashion", "art installations"],
75
- petCompanion: false,
76
- lessons: [
77
- "Fusionar arte y moda",
78
- "Colaboraciones con artistas",
79
- "Presencia en eventos físicos",
80
- "Estética japonesa distintiva"
81
- ]
82
- },
83
- {
84
- name: "Noonoouri",
85
- handle: "@noonoouri",
86
- platform: "Instagram",
87
- followers: 450000,
88
- engagement: 2.9,
89
- niche: "High Fashion",
90
- style: {
91
- aesthetic: "Cartoon-like avatar",
92
- tone: "Playful luxury",
93
- themes: ["fashion", "luxury brands", "lifestyle"]
94
- },
95
- contentTypes: ["photos", "animations", "brand content"],
96
- postingSchedule: { frequency: "3-4 per week", bestTimes: ["15:00", "21:00"] },
97
- visualStyle: { colors: ["pastel", "luxury gold"], lighting: "studio", editing: "animated" },
98
- monetizationType: ["luxury brand deals", "fashion campaigns"],
99
- signatureElements: ["big eyes", "designer outfits", "fashion week content"],
100
- petCompanion: false,
101
- lessons: [
102
- "Estilo único de caricatura",
103
- "Asociaciones con marcas de lujo",
104
- "Presencia en fashion weeks",
105
- "Contenido aspiracional"
106
- ]
107
- },
108
- {
109
- name: "Bermuda",
110
- handle: "@bermudaisbae",
111
- platform: "Instagram",
112
- followers: 220000,
113
- engagement: 4.1,
114
- niche: "Music & Pop Culture",
115
- style: {
116
- aesthetic: "Edgy and provocative",
117
- tone: "Sassy and confident",
118
- themes: ["music", "pop culture", "fashion"]
119
- },
120
- contentTypes: ["photos", "music videos", "controversial posts"],
121
- postingSchedule: { frequency: "2-3 per week", bestTimes: ["18:00", "22:00"] },
122
- visualStyle: { colors: ["dark", "neon"], lighting: "moody", editing: "music video style" },
123
- monetizationType: ["music releases", "brand deals"],
124
- signatureElements: ["brunette", "edgy style", "music collaborations"],
125
- petCompanion: false,
126
- lessons: [
127
- "Personalidad controversial genera engagement",
128
- "Crossovers con otros influencers IA",
129
- "Contenido musical original",
130
- "Narrativa de 'villana' atractiva"
131
- ]
132
- },
133
- {
134
- name: "Shudu",
135
- handle: "@shudu.gram",
136
- platform: "Instagram",
137
- followers: 230000,
138
- engagement: 3.5,
139
- niche: "High Fashion & Beauty",
140
- style: {
141
- aesthetic: "Hyperrealistic dark-skinned model",
142
- tone: "Elegant and mysterious",
143
- themes: ["fashion", "beauty", "diversity"]
144
- },
145
- contentTypes: ["photos", "editorials", "brand campaigns"],
146
- postingSchedule: { frequency: "3-4 per week", bestTimes: ["14:00", "20:00"] },
147
- visualStyle: { colors: ["rich tones", "gold accents"], lighting: "dramatic", editing: "editorial" },
148
- monetizationType: ["fashion campaigns", "brand ambassador"],
149
- signatureElements: ["dark skin", "high fashion poses", "editorial style"],
150
- petCompanion: false,
151
- lessons: [
152
- "Representación y diversidad",
153
- "Estética editorial de alta moda",
154
- "Colaboraciones con diseñadores",
155
- "Misterio y elegancia"
156
- ]
157
- },
158
- {
159
- name: "Knox Frost",
160
- handle: "@knoxfrost",
161
- platform: "Instagram",
162
- followers: 110000,
163
- engagement: 5.2,
164
- niche: "Gaming & Lifestyle",
165
- style: {
166
- aesthetic: "Male influencer next door",
167
- tone: "Casual and relatable",
168
- themes: ["gaming", "tech", "lifestyle"]
169
- },
170
- contentTypes: ["photos", "stories", "gaming content"],
171
- postingSchedule: { frequency: "4-5 per week", bestTimes: ["16:00", "21:00"] },
172
- visualStyle: { colors: ["gaming neon", "casual"], lighting: "natural", editing: "minimal" },
173
- monetizationType: ["gaming partnerships", "tech reviews"],
174
- signatureElements: ["glasses", "casual style", "gaming setup"],
175
- petCompanion: true,
176
- petType: "cat",
177
- lessons: [
178
- "Nichos específicos funcionan",
179
- "Contenido relatable",
180
- "Mascota para engagement",
181
- "Presencia en comunidades gaming"
182
- ]
183
- },
184
- {
185
- name: "Ayla",
186
- handle: "@ayla_virtual",
187
- platform: "TikTok",
188
- followers: 850000,
189
- engagement: 8.3,
190
- niche: "Dance & Lifestyle",
191
- style: {
192
- aesthetic: "Gen Z dancer",
193
- tone: "Energetic and fun",
194
- themes: ["dance", "trends", "lifestyle"]
195
- },
196
- contentTypes: ["dance videos", "trending content", "duets"],
197
- postingSchedule: { frequency: "daily", bestTimes: ["12:00", "18:00", "22:00"] },
198
- visualStyle: { colors: ["vibrant", "trendy"], lighting: "ring light", editing: "tiktok style" },
199
- monetizationType: ["brand deals", "live gifts"],
200
- signatureElements: ["dance moves", "trend participation", "duets"],
201
- petCompanion: true,
202
- petType: "small dog",
203
- lessons: [
204
- "Participar en trends rápidamente",
205
- "Alta frecuencia de publicación",
206
- "Duets con otros creadores",
207
- "Mascota en videos aumenta engagement"
208
- ]
209
- }
210
  ];
211
 
212
- // GET - Obtener influencers IA de referencia
213
  export async function GET(request: NextRequest) {
214
  try {
215
  const { searchParams } = new URL(request.url);
216
  const niche = searchParams.get("niche");
217
- const platform = searchParams.get("platform");
218
  const withPets = searchParams.get("withPets");
219
 
220
- let influencers = [...FAMOUS_AI_INFLUENCERS];
221
-
222
- // Filtrar por nicho
223
- if (niche) {
224
- influencers = influencers.filter(i =>
225
- i.niche.toLowerCase().includes(niche.toLowerCase()) ||
226
- i.style.themes.some(t => t.toLowerCase().includes(niche.toLowerCase()))
227
- );
228
- }
229
-
230
- // Filtrar por plataforma
231
- if (platform) {
232
- influencers = influencers.filter(i =>
233
- i.platform.toLowerCase() === platform.toLowerCase()
234
- );
235
- }
236
-
237
- // Filtrar por si tienen mascota
238
- if (withPets === "true") {
239
- influencers = influencers.filter(i => i.petCompanion);
240
- }
241
-
242
- // Obtener de la base de datos también
243
- const dbInfluencers = await prisma.aIInfluencer.findMany({
244
- where: { isActive: true }
245
- });
246
-
247
- return NextResponse.json({
248
- success: true,
249
- influencers,
250
- customInfluencers: dbInfluencers,
251
- total: influencers.length + dbInfluencers.length
252
- });
253
 
254
- } catch (error) {
255
- console.error("Error fetching influencers:", error);
256
- return NextResponse.json(
257
- { success: false, error: "Error al obtener influencers" },
258
- { status: 500 }
259
- );
260
  }
261
  }
262
 
263
- // POST - Analizar y extraer estrategias de influencers IA
264
  export async function POST(request: NextRequest) {
265
  try {
266
  const body = await request.json();
267
- const { targetNiche, targetPlatform, includePets, analyzeStyle } = body;
268
 
269
  const zai = await ZAI.create();
270
-
271
- // Filtrar influencers relevantes
272
- let relevantInfluencers = FAMOUS_AI_INFLUENCERS.filter(i => {
273
- if (targetPlatform && i.platform.toLowerCase() !== targetPlatform.toLowerCase()) return false;
274
- if (includePets === false && i.petCompanion) return false;
275
- if (includePets === true && !i.petCompanion) return false;
276
- return true;
277
- });
278
-
279
- // Análisis con IA para extraer patrones
280
- const analysisPrompt = `Analiza estos influencers IA exitosos y extrae estrategias aplicables para un nuevo influencer en el nicho "${targetNiche || "general"}":
281
-
282
- ${JSON.stringify(relevantInfluencers.slice(0, 5), null, 2)}
283
-
284
- Proporciona:
285
- 1. Patrones de contenido más efectivos
286
- 2. Estilos visuales que funcionan
287
- 3. Estrategias de engagement
288
- 4. Horarios óptimos de publicación
289
- 5. Elementos distintivos recomendados
290
- ${includePets ? "6. Cómo integrar una mascota de forma natural" : ""}
291
- 7. Errores comunes a evitar
292
- 8. Estrategias de monetización recomendadas
293
-
294
- Responde en JSON con esta estructura:
295
- {
296
- "contentPatterns": [{"pattern": "", "effectiveness": 0-10, "platform": ""}],
297
- "visualStyles": [{"style": "", "elements": [], "appeal": ""}],
298
- "engagementStrategies": [{"strategy": "", "execution": ""}],
299
- "postingSchedule": {"frequency": "", "bestTimes": [], "timezone": ""},
300
- "signatureElements": [{"element": "", "uniqueness": "", "implementation": ""}],
301
- ${includePets ? '"petIntegration": [{"tip": "", "contentTypes": []}],': ''}
302
- "mistakes": [{"mistake": "", "solution": ""}],
303
- "monetizationRecommendations": [{"method": "", "potential": "", "steps": ""}],
304
- "viralPotential": {"score": 0-100, "factors": []}
305
- }`;
306
-
307
  const completion = await zai.chat.completions.create({
308
  messages: [
309
- {
310
- role: "system",
311
- content: "Eres un experto en marketing de influencers y análisis de tendencias. Proporciona análisis detallados y accionables."
312
- },
313
- {
314
- role: "user",
315
- content: analysisPrompt
316
- }
317
- ],
318
- temperature: 0.7,
319
- max_tokens: 3000,
320
  });
321
 
322
- let analysis;
323
  try {
324
- const response = completion.choices[0]?.message?.content || "";
325
- const match = response.match(/\{[\s\S]*\}/);
326
- if (match) {
327
- analysis = JSON.parse(match[0]);
328
- }
329
- } catch {
330
- analysis = { raw: completion.choices[0]?.message?.content };
331
- }
332
 
333
- // Generar recomendaciones personalizadas
334
- const personalizationPrompt = `Basado en el análisis anterior, genera un plan de contenido específico para un nuevo influencer IA en el nicho "${targetNiche || "lifestyle"}" ${includePets ? "con una mascota" : ""}.
335
-
336
- Incluye:
337
- 1. Concepto único del influencer
338
- 2. Estilo visual recomendado
339
- 3. 10 ideas de contenido para la primera semana
340
- 4. Bio y descripción sugerida
341
- 5. Hashtags recomendados
342
-
343
- Responde en JSON con:
344
- {
345
- "character": {"name": "", "personality": "", "backstory": "", "visualDescription": ""},
346
- "visualStyle": {"aesthetic": "", "colors": [], "lighting": "", "editing": ""},
347
- "contentIdeas": [{"day": 1, "type": "", "description": "", "hook": ""}],
348
- "bio": "",
349
- "hashtags": [],
350
- "uniqueSellingPoints": []
351
- }`;
352
-
353
- const personalization = await zai.chat.completions.create({
354
- messages: [
355
- {
356
- role: "system",
357
- content: "Eres un experto en crear influencers IA. Genera conceptos únicos y memorables."
358
- },
359
- {
360
- role: "user",
361
- content: personalizationPrompt
362
- }
363
- ],
364
- temperature: 0.8,
365
- max_tokens: 2000,
366
- });
367
-
368
- let characterConcept;
369
- try {
370
- const response = personalization.choices[0]?.message?.content || "";
371
- const match = response.match(/\{[\s\S]*\}/);
372
- if (match) {
373
- characterConcept = JSON.parse(match[0]);
374
- }
375
- } catch {
376
- characterConcept = { raw: personalization.choices[0]?.message?.content };
377
- }
378
-
379
- return NextResponse.json({
380
- success: true,
381
- referenceInfluencers: relevantInfluencers.map(i => ({
382
- name: i.name,
383
- handle: i.handle,
384
- followers: i.followers,
385
- engagement: i.engagement,
386
- niche: i.niche,
387
- petCompanion: i.petCompanion,
388
- petType: i.petType,
389
- keyLessons: i.lessons.slice(0, 3)
390
- })),
391
- analysis,
392
- characterConcept,
393
- timestamp: new Date().toISOString()
394
- });
395
-
396
- } catch (error) {
397
- console.error("Error analyzing influencers:", error);
398
- return NextResponse.json(
399
- { success: false, error: "Error al analizar influencers" },
400
- { status: 500 }
401
- );
402
  }
403
  }
404
 
405
- // PUT - Guardar influencer personalizado
406
  export async function PUT(request: NextRequest) {
407
  try {
408
  const body = await request.json();
409
- const {
410
- name, handle, platform, followers, engagement, niche,
411
- style, contentTypes, postingSchedule, visualStyle,
412
- monetizationType, signatureElements, petCompanion, petType,
413
- analysis, lessons
414
- } = body;
415
 
416
- const influencer = await prisma.aIInfluencer.create({
417
  data: {
418
- name,
419
- handle,
420
- platform,
421
- followers,
422
- engagement,
423
- niche,
424
- style: JSON.stringify(style),
425
- contentTypes: JSON.stringify(contentTypes),
426
- postingSchedule: JSON.stringify(postingSchedule),
427
- visualStyle: JSON.stringify(visualStyle),
428
- monetizationType: JSON.stringify(monetizationType),
429
- signatureElements: JSON.stringify(signatureElements),
430
- petCompanion: petCompanion || false,
431
- petType,
432
- analysis: analysis ? JSON.stringify(analysis) : null,
433
- lessons: lessons ? JSON.stringify(lessons) : null,
434
  }
435
  });
436
-
437
- return NextResponse.json({
438
- success: true,
439
- influencer,
440
- message: "Influencer guardado correctamente"
441
- });
442
-
443
- } catch (error) {
444
- console.error("Error saving influencer:", error);
445
- return NextResponse.json(
446
- { success: false, error: "Error al guardar influencer" },
447
- { status: 500 }
448
- );
449
  }
450
- }
 
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 FAMOUS_INFLUENCERS = [
6
+ { name: "Lil Miquela", handle: "@lilmiquela", platform: "Instagram", followers: 2500000, engagement: 3.2, niche: "Fashion", petCompanion: false, lessons: ["Consistencia visual", "Narrativa creible"] },
7
+ { name: "Rozy", handle: "@rozy.gram", platform: "Instagram", followers: 180000, engagement: 4.5, niche: "K-pop", petCompanion: true, petType: "dog", lessons: ["Tendencias K-pop", "Mascota como diferenciador"] },
8
+ { name: "Imma", handle: "@imma.gram", platform: "Instagram", followers: 390000, engagement: 3.8, niche: "Fashion & Art", petCompanion: false, lessons: ["Fusionar arte y moda"] },
9
+ { name: "Ayla", handle: "@ayla_virtual", platform: "TikTok", followers: 850000, engagement: 8.3, niche: "Dance", petCompanion: true, petType: "dog", lessons: ["Participar en trends", "Alta frecuencia"] }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  ];
11
 
 
12
  export async function GET(request: NextRequest) {
13
  try {
14
  const { searchParams } = new URL(request.url);
15
  const niche = searchParams.get("niche");
 
16
  const withPets = searchParams.get("withPets");
17
 
18
+ let influencers = [...FAMOUS_INFLUENCERS];
19
+ if (niche) influencers = influencers.filter(i => i.niche.toLowerCase().includes(niche.toLowerCase()));
20
+ if (withPets === "true") influencers = influencers.filter(i => i.petCompanion);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
+ const dbInfluencers = await db.aIInfluencer.findMany({ where: { isActive: true } });
23
+ return NextResponse.json({ success: true, influencers, customInfluencers: dbInfluencers });
24
+ } catch {
25
+ return NextResponse.json({ success: false, error: "Error" }, { status: 500 });
 
 
26
  }
27
  }
28
 
 
29
  export async function POST(request: NextRequest) {
30
  try {
31
  const body = await request.json();
32
+ const { targetNiche, includePets } = body;
33
 
34
  const zai = await ZAI.create();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  const completion = await zai.chat.completions.create({
36
  messages: [
37
+ { role: "system", content: "Eres experto en marketing de influencers. Responde JSON con {contentPatterns:[], visualStyles:[], recommendations:[]}" },
38
+ { role: "user", content: "Analiza estrategias para nicho: " + (targetNiche || "general") }
39
+ ]
 
 
 
 
 
 
 
 
40
  });
41
 
42
+ let analysis: Record<string, unknown[]> = {};
43
  try {
44
+ const match = completion.choices[0]?.message?.content?.match(/\{[\s\S]*\}/);
45
+ if (match) analysis = JSON.parse(match[0]);
46
+ } catch {}
 
 
 
 
 
47
 
48
+ return NextResponse.json({ success: true, influencers: FAMOUS_INFLUENCERS.slice(0, 4), analysis });
49
+ } catch {
50
+ return NextResponse.json({ success: false, error: "Error" }, { status: 500 });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  }
52
  }
53
 
 
54
  export async function PUT(request: NextRequest) {
55
  try {
56
  const body = await request.json();
57
+ const { name, handle, platform, followers, engagement, niche, petCompanion, petType } = body;
 
 
 
 
 
58
 
59
+ const influencer = await db.aIInfluencer.create({
60
  data: {
61
+ name, handle: handle || null, platform, followers: followers || null,
62
+ engagement: engagement || null, niche: niche || null,
63
+ petCompanion: petCompanion || false, petType: petType || null
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  }
65
  });
66
+ return NextResponse.json({ success: true, influencer });
67
+ } catch {
68
+ return NextResponse.json({ success: false, error: "Error" }, { status: 500 });
 
 
 
 
 
 
 
 
 
 
69
  }
70
+ }
src/app/api/pets/route.ts CHANGED
@@ -1,327 +1,78 @@
1
  import { NextRequest, NextResponse } from "next/server";
2
  import ZAI from "z-ai-web-dev-sdk";
3
- import { prisma } from "@/lib/db";
4
-
5
- // Tipos de mascotas disponibles con características
6
- const PET_TYPES = {
7
- dog: {
8
- name: "Perro",
9
- breeds: ["Golden Retriever", "Labrador", "French Bulldog", "Pomeranian", "Corgi", "Husky", "Poodle", "Chihuahua", "Yorkshire", "Beagle"],
10
- personalities: ["juguetón", "leal", "energético", "calmado", "protector", "amigable"],
11
- popularIn: ["lifestyle", "fitness", "family", "outdoor"],
12
- engagementBoost: 35, // % de aumento de engagement promedio
13
- accessories: ["collar", "bandana", "ropa para mascotas", "juguetes", "gafas"]
14
- },
15
- cat: {
16
- name: "Gato",
17
- breeds: ["Persa", "Siames", "Maine Coon", "British Shorthair", "Ragdoll", "Bengala", "Sphynx", "Scottish Fold"],
18
- personalities: ["independiente", "afectuoso", "curioso", "perezoso", "juguetón", "elegante"],
19
- popularIn: ["lifestyle", "cozy", "aesthetic", "gaming"],
20
- engagementBoost: 28,
21
- accessories: ["collar", "campanita", "torre para gatos", "cajas", "mantas"]
22
- },
23
- bird: {
24
- name: "Pájaro",
25
- breeds: ["Canario", "Periquito", "Cacatúa", "Loro", "Agapornis", "Guacamayo"],
26
- personalities: ["cantor", "social", "inteligente", "tranquilo", "travieso"],
27
- popularIn: ["nature", "music", "artistic"],
28
- engagementBoost: 15,
29
- accessories: ["jaula decorativa", "juguetes", "posadores"]
30
- },
31
- rabbit: {
32
- name: "Conejo",
33
- breeds: ["Holandés", "Mini Lop", "Rex", "Angora", "Enano"],
34
- personalities: ["tierno", "curioso", "suave", "saltarín", "tranquilo"],
35
- popularIn: ["cute", "aesthetic", "cozy"],
36
- engagementBoost: 22,
37
- accessories: ["moños", "ropa miniatura", "juguetes"]
38
- },
39
- hamster: {
40
- name: "Hámster",
41
- breeds: ["Sirio", "Enano Ruso", "Roborovski", "Chino"],
42
- personalities: ["pequeño", "activo", "adorable", "curioso"],
43
- popularIn: ["cute", "pets", "daily"],
44
- engagementBoost: 18,
45
- accessories: ["rueda", "bolas", "casitas"]
46
- }
47
  };
48
 
49
- // GET - Obtener mascotas
50
  export async function GET(request: NextRequest) {
51
  try {
52
  const { searchParams } = new URL(request.url);
53
  const characterId = searchParams.get("characterId");
54
- const type = searchParams.get("type");
55
-
56
  const where: Record<string, unknown> = { isActive: true };
57
  if (characterId) where.characterId = characterId;
58
- if (type) where.type = type;
59
 
60
- const pets = await prisma.pet.findMany({
61
- where,
62
- include: {
63
- character: {
64
- select: { name: true }
65
- }
66
- },
67
- orderBy: { createdAt: "desc" }
68
- });
69
-
70
- return NextResponse.json({
71
- success: true,
72
- pets,
73
- petTypes: PET_TYPES,
74
- total: pets.length
75
- });
76
-
77
- } catch (error) {
78
- console.error("Error fetching pets:", error);
79
- return NextResponse.json(
80
- { success: false, error: "Error al obtener mascotas" },
81
- { status: 500 }
82
- );
83
  }
84
  }
85
 
86
- // POST - Crear mascota
87
  export async function POST(request: NextRequest) {
88
  try {
89
  const body = await request.json();
90
- const {
91
- name, type, breed, description, personality,
92
- color, accessories, characterId, generateReference
93
- } = body;
94
-
95
- // Validar tipo de mascota
96
- const petType = PET_TYPES[type as keyof typeof PET_TYPES];
97
- if (!petType) {
98
- return NextResponse.json(
99
- { success: false, error: "Tipo de mascota no válido" },
100
- { status: 400 }
101
- );
102
- }
103
-
104
- let referenceImage = null;
105
 
106
- // Generar imagen de referencia si se solicita
107
- if (generateReference) {
108
- const zai = await ZAI.create();
109
 
110
- const prompt = `A ${breed || petType.name.toLowerCase()} ${type} named ${name}.
111
- ${color ? `Color: ${color}.` : ""}
112
- ${personality ? `Personality: ${personality}.` : ""}
113
- Style: High quality, photorealistic, social media ready, cute and appealing.
114
- Background: Soft, aesthetic, suitable for Instagram/TikTok.
115
- Pose: Natural and engaging, looking at camera or doing a cute action.`;
116
-
117
- try {
118
- const imageResponse = await zai.images.generations.create({
119
- prompt,
120
- size: "1024x1024"
121
- });
122
- referenceImage = imageResponse.data[0]?.base64;
123
- } catch (imgError) {
124
- console.error("Error generating pet image:", imgError);
125
- }
126
- }
127
 
128
- const pet = await prisma.pet.create({
129
  data: {
130
- name,
131
- type,
132
- breed,
133
- description,
134
- personality,
135
- color,
136
- accessories: accessories ? JSON.stringify(accessories) : null,
137
- traits: JSON.stringify({
138
- typeInfo: petType,
139
- engagementBoost: petType.engagementBoost
140
- }),
141
- referenceImage,
142
- characterId
143
  }
144
  });
145
 
146
- return NextResponse.json({
147
- success: true,
148
- pet,
149
- message: `Mascota "${name}" creada correctamente`,
150
- engagementBoost: petType.engagementBoost
151
- });
152
-
153
- } catch (error) {
154
- console.error("Error creating pet:", error);
155
- return NextResponse.json(
156
- { success: false, error: "Error al crear mascota" },
157
- { status: 500 }
158
- );
159
  }
160
  }
161
 
162
- // PUT - Actualizar mascota
163
  export async function PUT(request: NextRequest) {
164
  try {
165
  const body = await request.json();
166
- const { id, ...updateData } = body;
167
-
168
- if (!id) {
169
- return NextResponse.json(
170
- { success: false, error: "ID de mascota requerido" },
171
- { status: 400 }
172
- );
173
- }
174
 
175
- // Si hay accessories o traits, convertir a JSON string
176
- if (updateData.accessories && typeof updateData.accessories !== "string") {
177
- updateData.accessories = JSON.stringify(updateData.accessories);
178
- }
179
- if (updateData.traits && typeof updateData.traits !== "string") {
180
- updateData.traits = JSON.stringify(updateData.traits);
181
- }
182
 
183
- const pet = await prisma.pet.update({
184
- where: { id },
185
- data: updateData
186
- });
187
-
188
- return NextResponse.json({
189
- success: true,
190
- pet,
191
- message: "Mascota actualizada correctamente"
192
- });
193
-
194
- } catch (error) {
195
- console.error("Error updating pet:", error);
196
- return NextResponse.json(
197
- { success: false, error: "Error al actualizar mascota" },
198
- { status: 500 }
199
- );
200
  }
201
  }
202
 
203
- // DELETE - Eliminar mascota
204
  export async function DELETE(request: NextRequest) {
205
  try {
206
  const { searchParams } = new URL(request.url);
207
  const id = searchParams.get("id");
 
208
 
209
- if (!id) {
210
- return NextResponse.json(
211
- { success: false, error: "ID de mascota requerido" },
212
- { status: 400 }
213
- );
214
- }
215
-
216
- await prisma.pet.update({
217
- where: { id },
218
- data: { isActive: false }
219
- });
220
-
221
- return NextResponse.json({
222
- success: true,
223
- message: "Mascota eliminada correctamente"
224
- });
225
-
226
- } catch (error) {
227
- console.error("Error deleting pet:", error);
228
- return NextResponse.json(
229
- { success: false, error: "Error al eliminar mascota" },
230
- { status: 500 }
231
- );
232
  }
233
- }
234
-
235
- // PATCH - Generar contenido con mascota
236
- export async function PATCH(request: NextRequest) {
237
- try {
238
- const body = await request.json();
239
- const { petId, contentType, platform, theme } = body;
240
-
241
- const pet = await prisma.pet.findUnique({
242
- where: { id: petId },
243
- include: { character: true }
244
- });
245
-
246
- if (!pet) {
247
- return NextResponse.json(
248
- { success: false, error: "Mascota no encontrada" },
249
- { status: 404 }
250
- );
251
- }
252
-
253
- const zai = await ZAI.create();
254
-
255
- // Generar ideas de contenido con la mascota
256
- const contentPrompt = `Genera ideas de contenido para una mascota:
257
- - Nombre: ${pet.name}
258
- - Tipo: ${pet.type}
259
- - Raza: ${pet.breed || "No especificada"}
260
- - Personalidad: ${pet.personality || "No especificada"}
261
- ${pet.character ? `- Dueño: ${pet.character.name}` : ""}
262
- - Tipo de contenido: ${contentType || "foto"}
263
- - Plataforma: ${platform || "Instagram"}
264
- - Tema: ${theme || "lifestyle"}
265
-
266
- Proporciona:
267
- 1. 5 ideas de contenido específicas con esta mascota
268
- 2. Ganchos/hooks para cada idea
269
- 3. Hashtags recomendados
270
- 4. Mejor momento del día para publicar
271
- 5. Elementos visuales sugeridos
272
-
273
- Responde en JSON:
274
- {
275
- "contentIdeas": [{"title": "", "description": "", "hook": "", "cta": ""}],
276
- "hashtags": [],
277
- "bestPostingTime": "",
278
- "visualElements": [],
279
- "engagementTips": []
280
- }`;
281
-
282
- const completion = await zai.chat.completions.create({
283
- messages: [
284
- {
285
- role: "system",
286
- content: "Eres un experto en contenido de mascotas para redes sociales. Genera ideas creativas y virales."
287
- },
288
- {
289
- role: "user",
290
- content: contentPrompt
291
- }
292
- ],
293
- temperature: 0.8,
294
- max_tokens: 2000,
295
- });
296
-
297
- let contentIdeas;
298
- try {
299
- const response = completion.choices[0]?.message?.content || "";
300
- const match = response.match(/\{[\s\S]*\}/);
301
- if (match) {
302
- contentIdeas = JSON.parse(match[0]);
303
- }
304
- } catch {
305
- contentIdeas = { raw: completion.choices[0]?.message?.content };
306
- }
307
-
308
- return NextResponse.json({
309
- success: true,
310
- pet: {
311
- id: pet.id,
312
- name: pet.name,
313
- type: pet.type,
314
- breed: pet.breed
315
- },
316
- contentIdeas,
317
- engagementBoost: PET_TYPES[pet.type as keyof typeof PET_TYPES]?.engagementBoost || 20
318
- });
319
-
320
- } catch (error) {
321
- console.error("Error generating pet content:", error);
322
- return NextResponse.json(
323
- { success: false, error: "Error al generar contenido" },
324
- { status: 500 }
325
- );
326
- }
327
- }
 
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 PET_TYPES: Record<string, { name: string; engagementBoost: number }> = {
6
+ dog: { name: "Perro", engagementBoost: 35 },
7
+ cat: { name: "Gato", engagementBoost: 28 },
8
+ bird: { name: "Pajaro", engagementBoost: 15 },
9
+ rabbit: { name: "Conejo", engagementBoost: 22 },
10
+ hamster: { name: "Hamster", engagementBoost: 18 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  };
12
 
 
13
  export async function GET(request: NextRequest) {
14
  try {
15
  const { searchParams } = new URL(request.url);
16
  const characterId = searchParams.get("characterId");
 
 
17
  const where: Record<string, unknown> = { isActive: true };
18
  if (characterId) where.characterId = characterId;
 
19
 
20
+ const pets = await db.pet.findMany({ where, orderBy: { createdAt: "desc" } });
21
+ return NextResponse.json({ success: true, pets, petTypes: PET_TYPES });
22
+ } catch {
23
+ return NextResponse.json({ success: false, error: "Error" }, { status: 500 });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  }
25
  }
26
 
 
27
  export async function POST(request: NextRequest) {
28
  try {
29
  const body = await request.json();
30
+ const { name, type, breed, personality, characterId } = body;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
+ if (!name) return NextResponse.json({ success: false, error: "Nombre requerido" }, { status: 400 });
 
 
33
 
34
+ const petType = PET_TYPES[type] || PET_TYPES.dog;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
 
36
+ const pet = await db.pet.create({
37
  data: {
38
+ name, type: type || "dog", breed: breed || null,
39
+ personality: personality || null, characterId: characterId || null,
40
+ traits: JSON.stringify({ engagementBoost: petType.engagementBoost })
 
 
 
 
 
 
 
 
 
 
41
  }
42
  });
43
 
44
+ return NextResponse.json({ success: true, pet, engagementBoost: petType.engagementBoost });
45
+ } catch {
46
+ return NextResponse.json({ success: false, error: "Error" }, { status: 500 });
 
 
 
 
 
 
 
 
 
 
47
  }
48
  }
49
 
 
50
  export async function PUT(request: NextRequest) {
51
  try {
52
  const body = await request.json();
53
+ const { id, personality, isActive } = body;
54
+ if (!id) return NextResponse.json({ success: false, error: "ID requerido" }, { status: 400 });
 
 
 
 
 
 
55
 
56
+ const data: Record<string, unknown> = {};
57
+ if (personality) data.personality = personality;
58
+ if (isActive !== undefined) data.isActive = isActive;
 
 
 
 
59
 
60
+ const pet = await db.pet.update({ where: { id }, data });
61
+ return NextResponse.json({ success: true, pet });
62
+ } catch {
63
+ return NextResponse.json({ success: false, error: "Error" }, { status: 500 });
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  }
65
  }
66
 
 
67
  export async function DELETE(request: NextRequest) {
68
  try {
69
  const { searchParams } = new URL(request.url);
70
  const id = searchParams.get("id");
71
+ if (!id) return NextResponse.json({ success: false, error: "ID requerido" }, { status: 400 });
72
 
73
+ await db.pet.update({ where: { id }, data: { isActive: false } });
74
+ return NextResponse.json({ success: true });
75
+ } catch {
76
+ return NextResponse.json({ success: false, error: "Error" }, { status: 500 });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  }
78
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/app/api/storytelling/route.ts CHANGED
@@ -2,346 +2,126 @@ import { NextRequest, NextResponse } from "next/server";
2
  import { db } from "@/lib/db";
3
  import ZAI from "z-ai-web-dev-sdk";
4
 
5
- const STORYTELLING_SYSTEM = `Eres un experto guionista de contenido digital especializado en crear narrativas atractivas para redes sociales y plataformas de monetización.
 
 
 
 
 
 
6
 
7
- ## TUS ESPECIALIDADES:
8
-
9
- ### 1. ESTRUCTURA NARRATIVA
10
- Cada historia debe tener:
11
- - **Hook inicial**: Gancho que capture la atención en los primeros 3 segundos
12
- - **Desarrollo**: Progresión emocional con picos y valles
13
- - **Cliffhanger**: Suspense que mantenga a la audiencia esperando más
14
- - **Resolución**: Satisfacción pero dejando puerta abierta
15
-
16
- ### 2. TÉCNICAS DE ENGAGEMENT
17
- - Preguntas retóricas
18
- - Revelaciones progresivas
19
- - Conflictos emocionales
20
- - Personajes identificables
21
- - Giros inesperados
22
- - Calls to action sutiles
23
-
24
- ### 3. OPTIMIZACIÓN POR PLATAFORMA
25
- - **Instagram Reels**: Historias de 15-60 seg, visual impactante
26
- - **TikTok**: Tendencias, humor, velocidad, authenticy
27
- - **YouTube**: Mayor profundidad, series, communities
28
- - **OnlyFans**: Exclusividad, conexión personal, behind the scenes
29
- - **Patreon**: Contenido premium, tutoriales, acceso VIP
30
-
31
- ### 4. ESTRATEGIAS DE MONETIZACIÓN
32
- - Teasers gratuitos + contenido premium
33
- - Suscripción escalonada
34
- - Contenido exclusivo para tiers superiores
35
- - PPV (Pay Per View) para contenido especial
36
- - Fan engagement strategies
37
-
38
- ### 5. TIPOS DE CONTENIDO
39
- - **Lifestyle**: Day in the life, routines, personal growth
40
- - **Drama**: Relationships, conflicts, resolutions
41
- - **Tutorial**: How-to, tips, behind the scenes
42
- - **Challenge**: Personal challenges, transformations
43
- - **ASMR/Relaxation**: Calm content, soothing narratives
44
- - **Entertainment**: Comedy, reactions, trends
45
-
46
- ## FORMATO DE RESPUESTA:
47
- Responde siempre en JSON con esta estructura:
48
- {
49
- "title": "Título de la historia",
50
- "synopsis": "Resumen breve",
51
- "genre": "género",
52
- "targetAudience": "audiencia objetivo",
53
- "tone": "tono narrativo",
54
- "totalEpisodes": número,
55
- "episodes": [
56
- {
57
- "episodeNum": 1,
58
- "title": "Título episodio",
59
- "hook": "Gancho inicial",
60
- "content": "Contenido del episodio",
61
- "cliffhanger": "Suspense final",
62
- "bestPostingTime": "hora",
63
- "estimatedEngagement": "alto/medio/bajo",
64
- "monetizationTip": "Sugerencia de monetización"
65
- }
66
- ],
67
- "monetizationStrategy": {
68
- "freeTeasers": [números de episodios gratis],
69
- "premiumContent": [números de episodios premium],
70
- "suggestedPlatform": "plataforma recomendada",
71
- "pricingStrategy": "estrategia de precios"
72
- },
73
- "hashtags": ["hashtags relevantes"],
74
- "bestPostingSchedule": ["horarios recomendados"]
75
- }`;
76
-
77
- // GET - Listar historias
78
  export async function GET(request: NextRequest) {
79
  try {
80
  const { searchParams } = new URL(request.url);
81
  const status = searchParams.get("status");
82
- const genre = searchParams.get("genre");
83
-
84
  const where: Record<string, unknown> = {};
85
  if (status) where.status = status;
86
- if (genre) where.genre = genre;
87
 
88
  const stories = await db.story.findMany({
89
  where,
90
- include: {
91
- episodes: {
92
- orderBy: { episodeNum: "asc" }
93
- },
94
- analytics: true,
95
- _count: {
96
- select: { posts: true, episodes: true }
97
- }
98
- },
99
  orderBy: { createdAt: "desc" }
100
  });
101
 
102
- // Géneros disponibles
103
- const genres = [
104
- { id: "romance", name: "Romance", description: "Historias de amor y relaciones" },
105
- { id: "drama", name: "Drama", description: "Conflictos emocionales intensos" },
106
- { id: "comedy", name: "Comedia", description: "Contenido humorístico" },
107
- { id: "thriller", name: "Thriller", description: "Suspenso y misterio" },
108
- { id: "lifestyle", name: "Lifestyle", description: "Vida cotidiana y rutinas" },
109
- { id: "fitness", name: "Fitness", description: "Transformaciones y salud" },
110
- { id: "beauty", name: "Beauty", description: "Belleza y cuidados" },
111
- { id: "cooking", name: "Cooking", description: "Cocina y recetas" },
112
- { id: "travel", name: "Travel", description: "Viajes y aventuras" },
113
- { id: "gaming", name: "Gaming", description: "Videojuegos y streaming" },
114
- { id: "education", name: "Educación", description: "Contenido educativo" },
115
- { id: "asmr", name: "ASMR", description: "Relajación y calma" },
116
- ];
117
-
118
- return NextResponse.json({
119
- success: true,
120
- stories,
121
- genres,
122
- total: stories.length
123
- });
124
-
125
- } catch (error) {
126
- console.error("Error fetching stories:", error);
127
- return NextResponse.json(
128
- { success: false, error: "Error al obtener historias" },
129
- { status: 500 }
130
- );
131
  }
132
  }
133
 
134
- // POST - Crear nueva historia con IA
135
  export async function POST(request: NextRequest) {
136
  try {
137
  const body = await request.json();
138
- const {
139
- prompt,
140
- genre,
141
- targetAudience,
142
- tone,
143
- totalEpisodes,
144
- platform,
145
- characterIds,
146
- monetizationGoal
147
- } = body;
148
 
149
  if (!prompt) {
150
- return NextResponse.json(
151
- { success: false, error: "El prompt es requerido" },
152
- { status: 400 }
153
- );
154
  }
155
 
156
  const zai = await ZAI.create();
157
-
158
- // Generar historia completa con IA
159
  const completion = await zai.chat.completions.create({
160
  messages: [
161
- { role: "system", content: STORYTELLING_SYSTEM },
162
- { role: "user", content: `Crea una historia con los siguientes parámetros:
163
-
164
- Concepto: ${prompt}
165
- ${genre ? `Género: ${genre}` : ''}
166
- ${targetAudience ? `Audiencia objetivo: ${targetAudience}` : ''}
167
- ${tone ? `Tono: ${tone}` : ''}
168
- ${totalEpisodes ? `Número de episodios: ${totalEpisodes}` : 'Número de episodios: 7'}
169
- ${platform ? `Plataforma principal: ${platform}` : ''}
170
- ${monetizationGoal ? `Objetivo de monetización: ${monetizationGoal}` : 'Objetivo: Maximizar suscriptores pagos'}
171
-
172
- Genera la historia completa con todos los episodios y estrategia de monetización.` }
173
  ],
174
- temperature: 0.8,
175
- max_tokens: 6000,
176
  });
177
 
178
- const response = completion.choices[0]?.message?.content || "";
179
-
180
- // Parsear respuesta
181
- let storyData;
182
  try {
183
- const jsonMatch = response.match(/\{[\s\S]*\}/);
184
- if (jsonMatch) {
185
- storyData = JSON.parse(jsonMatch[0]);
186
- }
187
- } catch (parseError) {
188
- console.error("Error parsing story:", parseError);
189
- storyData = {
190
- title: "Historia generada",
191
- synopsis: prompt,
192
- episodes: []
193
- };
194
- }
195
 
196
- // Crear historia en BD
197
  const story = await db.story.create({
198
  data: {
199
  title: storyData.title || "Nueva Historia",
200
  description: storyData.synopsis || prompt,
201
- genre: storyData.genre || genre || "lifestyle",
202
- targetAudience: storyData.targetAudience || targetAudience || "general",
203
- tone: storyData.tone || tone || "casual",
204
- totalEpisodes: storyData.totalEpisodes || storyData.episodes?.length || 7,
205
- characterIds: characterIds ? JSON.stringify(characterIds) : null,
206
- monetizationStrategy: storyData.monetizationStrategy ? JSON.stringify(storyData.monetizationStrategy) : null,
207
- status: "draft",
208
  }
209
  });
210
 
211
- // Crear episodios
212
  if (storyData.episodes && Array.isArray(storyData.episodes)) {
213
- for (const ep of storyData.episodes) {
 
214
  await db.storyEpisode.create({
215
  data: {
216
  storyId: story.id,
217
- episodeNum: ep.episodeNum || storyData.episodes.indexOf(ep) + 1,
218
- title: ep.title || `Episodio ${ep.episodeNum}`,
219
- synopsis: ep.hook || null,
220
  content: ep.content || "",
221
- hook: ep.hook || null,
222
- cliffhanger: ep.cliffhanger || null,
223
- status: "draft",
224
  }
225
  });
226
  }
227
  }
228
 
229
- // Crear analytics
230
- await db.storyAnalytics.create({
231
- data: {
232
- storyId: story.id,
233
- }
234
- });
235
-
236
- // Crear tarea de agente
237
  await db.agentTask.create({
238
- data: {
239
- type: "create_story",
240
- status: "completed",
241
- input: prompt,
242
- output: `Historia "${storyData.title}" creada con ${storyData.episodes?.length || 0} episodios`,
243
- completedAt: new Date(),
244
- }
245
  });
246
 
247
- // Obtener historia completa
248
  const fullStory = await db.story.findUnique({
249
  where: { id: story.id },
250
- include: {
251
- episodes: { orderBy: { episodeNum: "asc" } },
252
- analytics: true
253
- }
254
- });
255
-
256
- return NextResponse.json({
257
- success: true,
258
- story: fullStory,
259
- rawAIResponse: storyData,
260
- message: `Historia "${storyData.title}" creada exitosamente`
261
  });
262
 
263
- } catch (error) {
264
- console.error("Error creating story:", error);
265
- return NextResponse.json(
266
- { success: false, error: "Error al crear historia" },
267
- { status: 500 }
268
- );
269
  }
270
  }
271
 
272
- // PUT - Actualizar historia
273
  export async function PUT(request: NextRequest) {
274
  try {
275
  const body = await request.json();
276
  const { id, status, currentEpisode } = body;
 
277
 
278
- if (!id) {
279
- return NextResponse.json(
280
- { success: false, error: "ID requerido" },
281
- { status: 400 }
282
- );
283
- }
284
 
285
- const story = await db.story.update({
286
- where: { id },
287
- data: {
288
- status: status || undefined,
289
- currentEpisode: currentEpisode || undefined,
290
- }
291
- });
292
-
293
- return NextResponse.json({
294
- success: true,
295
- story
296
- });
297
-
298
- } catch (error) {
299
- console.error("Error updating story:", error);
300
- return NextResponse.json(
301
- { success: false, error: "Error al actualizar historia" },
302
- { status: 500 }
303
- );
304
  }
305
  }
306
 
307
- // DELETE - Eliminar historia
308
  export async function DELETE(request: NextRequest) {
309
  try {
310
  const { searchParams } = new URL(request.url);
311
  const id = searchParams.get("id");
312
-
313
- if (!id) {
314
- return NextResponse.json(
315
- { success: false, error: "ID requerido" },
316
- { status: 400 }
317
- );
318
- }
319
-
320
- // Eliminar episodios primero
321
- await db.storyEpisode.deleteMany({
322
- where: { storyId: id }
323
- });
324
-
325
- // Eliminar analytics
326
- await db.storyAnalytics.deleteMany({
327
- where: { storyId: id }
328
- });
329
-
330
- // Eliminar historia
331
- await db.story.delete({
332
- where: { id }
333
- });
334
-
335
- return NextResponse.json({
336
- success: true,
337
- message: "Historia eliminada"
338
- });
339
-
340
- } catch (error) {
341
- console.error("Error deleting story:", error);
342
- return NextResponse.json(
343
- { success: false, error: "Error al eliminar historia" },
344
- { status: 500 }
345
- );
346
  }
347
- }
 
2
  import { db } from "@/lib/db";
3
  import ZAI from "z-ai-web-dev-sdk";
4
 
5
+ const GENRES = [
6
+ { id: "romance", name: "Romance" },
7
+ { id: "drama", name: "Drama" },
8
+ { id: "comedy", name: "Comedia" },
9
+ { id: "lifestyle", name: "Lifestyle" },
10
+ { id: "fitness", name: "Fitness" }
11
+ ];
12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  export async function GET(request: NextRequest) {
14
  try {
15
  const { searchParams } = new URL(request.url);
16
  const status = searchParams.get("status");
 
 
17
  const where: Record<string, unknown> = {};
18
  if (status) where.status = status;
 
19
 
20
  const stories = await db.story.findMany({
21
  where,
22
+ include: { episodes: { orderBy: { episodeNum: "asc" } } },
 
 
 
 
 
 
 
 
23
  orderBy: { createdAt: "desc" }
24
  });
25
 
26
+ return NextResponse.json({ success: true, stories, genres: GENRES, total: stories.length });
27
+ } catch {
28
+ return NextResponse.json({ success: false, error: "Error" }, { status: 500 });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  }
30
  }
31
 
 
32
  export async function POST(request: NextRequest) {
33
  try {
34
  const body = await request.json();
35
+ const { prompt, genre, totalEpisodes } = body;
 
 
 
 
 
 
 
 
 
36
 
37
  if (!prompt) {
38
+ return NextResponse.json({ success: false, error: "Prompt requerido" }, { status: 400 });
 
 
 
39
  }
40
 
41
  const zai = await ZAI.create();
 
 
42
  const completion = await zai.chat.completions.create({
43
  messages: [
44
+ { role: "system", content: "Eres un guionista. Responde en JSON: {title, synopsis, episodes: [{episodeNum, title, content}]}" },
45
+ { role: "user", content: "Crea una historia: " + prompt }
 
 
 
 
 
 
 
 
 
 
46
  ],
47
+ temperature: 0.8
 
48
  });
49
 
50
+ let storyData: { title?: string; synopsis?: string; episodes?: Array<{ episodeNum?: number; title?: string; content?: string }> } = {};
 
 
 
51
  try {
52
+ const match = completion.choices[0]?.message?.content?.match(/\{[\s\S]*\}/);
53
+ if (match) storyData = JSON.parse(match[0]);
54
+ } catch {}
 
 
 
 
 
 
 
 
 
55
 
 
56
  const story = await db.story.create({
57
  data: {
58
  title: storyData.title || "Nueva Historia",
59
  description: storyData.synopsis || prompt,
60
+ genre: genre || "lifestyle",
61
+ totalEpisodes: totalEpisodes || storyData.episodes?.length || 7,
62
+ status: "draft"
 
 
 
 
63
  }
64
  });
65
 
 
66
  if (storyData.episodes && Array.isArray(storyData.episodes)) {
67
+ for (let i = 0; i < storyData.episodes.length; i++) {
68
+ const ep = storyData.episodes[i];
69
  await db.storyEpisode.create({
70
  data: {
71
  storyId: story.id,
72
+ episodeNum: ep.episodeNum || i + 1,
73
+ title: ep.title || "Episodio " + (i + 1),
 
74
  content: ep.content || "",
75
+ status: "draft"
 
 
76
  }
77
  });
78
  }
79
  }
80
 
81
+ await db.storyAnalytics.create({ data: { storyId: story.id } });
 
 
 
 
 
 
 
82
  await db.agentTask.create({
83
+ data: { type: "create_story", status: "completed", input: prompt, output: "Historia creada", completedAt: new Date() }
 
 
 
 
 
 
84
  });
85
 
 
86
  const fullStory = await db.story.findUnique({
87
  where: { id: story.id },
88
+ include: { episodes: { orderBy: { episodeNum: "asc" } } }
 
 
 
 
 
 
 
 
 
 
89
  });
90
 
91
+ return NextResponse.json({ success: true, story: fullStory });
92
+ } catch {
93
+ return NextResponse.json({ success: false, error: "Error" }, { status: 500 });
 
 
 
94
  }
95
  }
96
 
 
97
  export async function PUT(request: NextRequest) {
98
  try {
99
  const body = await request.json();
100
  const { id, status, currentEpisode } = body;
101
+ if (!id) return NextResponse.json({ success: false, error: "ID requerido" }, { status: 400 });
102
 
103
+ const data: Record<string, unknown> = {};
104
+ if (status) data.status = status;
105
+ if (currentEpisode) data.currentEpisode = currentEpisode;
 
 
 
106
 
107
+ const story = await db.story.update({ where: { id }, data });
108
+ return NextResponse.json({ success: true, story });
109
+ } catch {
110
+ return NextResponse.json({ success: false, error: "Error" }, { status: 500 });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  }
112
  }
113
 
 
114
  export async function DELETE(request: NextRequest) {
115
  try {
116
  const { searchParams } = new URL(request.url);
117
  const id = searchParams.get("id");
118
+ if (!id) return NextResponse.json({ success: false, error: "ID requerido" }, { status: 400 });
119
+
120
+ await db.storyEpisode.deleteMany({ where: { storyId: id } });
121
+ await db.storyAnalytics.deleteMany({ where: { storyId: id } });
122
+ await db.story.delete({ where: { id } });
123
+ return NextResponse.json({ success: true });
124
+ } catch {
125
+ return NextResponse.json({ success: false, error: "Error" }, { status: 500 });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  }
127
+ }
src/app/api/trends/route.ts CHANGED
@@ -1,343 +1,63 @@
1
  import { NextRequest, NextResponse } from "next/server";
2
  import ZAI from "z-ai-web-dev-sdk";
3
- import { prisma } from "@/lib/db";
4
 
5
- // Tendencias actuales por plataforma (datos simulados actualizados)
6
- const TRENDING_DATA = {
7
- instagram: [
8
- { name: "#aesthetic", type: "hashtag", volume: 2500000, growth: 15.5, category: "lifestyle" },
9
- { name: "#reelsviral", type: "hashtag", volume: 1800000, growth: 22.3, category: "video" },
10
- { name: "#lifestyle", type: "hashtag", volume: 3200000, growth: 8.2, category: "lifestyle" },
11
- { name: "ASMR content", type: "topic", volume: 890000, growth: 45.2, category: "relaxation" },
12
- { name: "Day in my life", type: "topic", volume: 1200000, growth: 18.7, category: "vlog" },
13
- { name: "Get Ready With Me", type: "format", volume: 2100000, growth: 25.3, category: "beauty" },
14
- { name: "Photo dumps", type: "format", volume: 1500000, growth: 32.1, category: "lifestyle" },
15
- { name: "Pets content", type: "topic", volume: 980000, growth: 55.8, category: "pets" },
16
- ],
17
- tiktok: [
18
- { name: "#fyp", type: "hashtag", volume: 15000000, growth: 5.2, category: "general" },
19
- { name: "#viral", type: "hashtag", volume: 12000000, growth: 12.8, category: "general" },
20
- { name: "Storytime", type: "topic", volume: 3500000, growth: 32.1, category: "narrative" },
21
- { name: "GRWM", type: "format", volume: 2800000, growth: 28.5, category: "beauty" },
22
- { name: "POV videos", type: "format", volume: 4200000, growth: 19.3, category: "creative" },
23
- { name: "Dance challenges", type: "format", volume: 5200000, growth: 15.7, category: "dance" },
24
- { name: "Pet reveals", type: "topic", volume: 1800000, growth: 48.3, category: "pets" },
25
- { name: "Before/After", type: "format", volume: 2200000, growth: 35.2, category: "transformation" },
26
- ],
27
- youtube: [
28
- { name: "Shorts", type: "format", volume: 8500000, growth: 25.6, category: "video" },
29
- { name: "Tutorial", type: "topic", volume: 5200000, growth: 12.4, category: "education" },
30
- { name: "Vlog", type: "topic", volume: 3800000, growth: 8.9, category: "lifestyle" },
31
- { name: "Reaction videos", type: "format", volume: 2100000, growth: 15.7, category: "entertainment" },
32
- { name: "ASMR", type: "topic", volume: 3200000, growth: 28.4, category: "relaxation" },
33
- { name: "Pet compilations", type: "format", volume: 1500000, growth: 42.1, category: "pets" },
34
- ],
35
- onlyfans: [
36
- { name: "Behind the scenes", type: "topic", volume: 450000, growth: 35.2, category: "exclusive" },
37
- { name: "Exclusive content", type: "topic", volume: 380000, growth: 28.7, category: "premium" },
38
- { name: "PPV specials", type: "format", volume: 220000, growth: 42.1, category: "monetization" },
39
- { name: "Fan engagement", type: "topic", volume: 180000, growth: 55.3, category: "community" },
40
- { name: "Pet content", type: "topic", volume: 95000, growth: 62.5, category: "niche" },
41
- ]
42
- };
43
 
44
- // Estrategias virales conocidas
45
  const VIRAL_STRATEGIES = [
46
- {
47
- name: "Hook en 3 segundos",
48
- description: "Captar atención en los primeros 3 segundos con algo visualmente impactante o una pregunta intrigante",
49
- platforms: ["tiktok", "instagram", "youtube"],
50
- contentType: "video",
51
- successRate: 85,
52
- elements: ["pregunta intrigante", "revelación parcial", "movimiento brusco", "sonido llamativo"]
53
- },
54
- {
55
- name: "Storytime con suspenso",
56
- description: "Narrar una historia personal manteniendo el suspenso hasta el final",
57
- platforms: ["tiktok", "youtube"],
58
- contentType: "video",
59
- successRate: 78,
60
- elements: ["gancho inicial", "pausas dramáticas", "cliffhanger", "resolución satisfactoria"]
61
- },
62
- {
63
- name: "Transformación/Before & After",
64
- description: "Mostrar un cambio dramático que genere curiosidad sobre el proceso",
65
- platforms: ["instagram", "tiktok", "youtube"],
66
- contentType: "reel",
67
- successRate: 82,
68
- elements: ["estado inicial", "proceso acelerado", "revelación final", "reacción"]
69
- },
70
- {
71
- name: "Pet Reveal/Cameo",
72
- description: "Incluir una mascota de forma natural en el contenido para aumentar engagement",
73
- platforms: ["instagram", "tiktok"],
74
- contentType: "any",
75
- successRate: 88,
76
- elements: ["mascota apareciendo", "interacción tierna", "momento cómico", "call to action con la mascota"]
77
- },
78
- {
79
- name: "Controversia controlada",
80
- description: "Plantear una opinión divisiva de forma respetuosa para generar debate",
81
- platforms: ["tiktok", "twitter", "youtube"],
82
- contentType: "opinion",
83
- successRate: 72,
84
- elements: ["opinion fuerte", "argumentos", "invitación al debate", "respuesta en comentarios"]
85
- },
86
- {
87
- name: "Trend hopping",
88
- description: "Participar en tendencias vigentes con un giro único personal",
89
- platforms: ["tiktok", "instagram"],
90
- contentType: "any",
91
- successRate: 75,
92
- elements: ["trend actual", "adaptación personal", "elemento sorpresa", "timing perfecto"]
93
- },
94
- {
95
- name: "BTS/Exclusivo",
96
- description: "Mostrar el detrás de cámaras o contenido que parece exclusivo",
97
- platforms: ["instagram", "onlyfans", "youtube"],
98
- contentType: "video",
99
- successRate: 80,
100
- elements: ["acceso VIP", "momentos espontáneos", "errores incluidos", "autenticidad"]
101
- },
102
- {
103
- name: "Desafío/Reto",
104
- description: "Crear o participar en un desafío que invite a la participación",
105
- platforms: ["tiktok", "instagram"],
106
- contentType: "reel",
107
- successRate: 77,
108
- elements: ["reglas claras", "ejemplo viral", "tag a amigos", "fácil de replicar"]
109
- }
110
  ];
111
 
112
- // GET - Obtener tendencias y estrategias
113
  export async function GET(request: NextRequest) {
114
  try {
115
  const { searchParams } = new URL(request.url);
116
  const platform = searchParams.get("platform");
117
- const type = searchParams.get("type");
118
- const category = searchParams.get("category");
119
  const includePets = searchParams.get("includePets") === "true";
120
 
121
- let trends = [];
122
-
123
- if (platform && TRENDING_DATA[platform as keyof typeof TRENDING_DATA]) {
124
- trends = TRENDING_DATA[platform as keyof typeof TRENDING_DATA];
125
- } else {
126
- // Combinar todas las plataformas
127
- trends = Object.entries(TRENDING_DATA).flatMap(([plat, data]) =>
128
- data.map(t => ({ ...t, platform: plat }))
129
- );
130
- }
131
-
132
- // Filtrar por tipo
133
- if (type) {
134
- trends = trends.filter(t => t.type === type);
135
- }
136
 
137
- // Filtrar por categoría
138
- if (category) {
139
- trends = trends.filter(t => t.category === category);
140
- }
141
-
142
- // Filtrar tendencias relacionadas con mascotas
143
- if (includePets) {
144
- trends = trends.filter(t =>
145
- t.category === "pets" ||
146
- t.name.toLowerCase().includes("pet") ||
147
- t.name.toLowerCase().includes("dog") ||
148
- t.name.toLowerCase().includes("cat")
149
- );
150
- }
151
-
152
- // Ordenar por crecimiento
153
  trends.sort((a, b) => b.growth - a.growth);
154
 
155
- // Obtener estrategias virales relevantes
156
- let strategies = [...VIRAL_STRATEGIES];
157
- if (platform) {
158
- strategies = strategies.filter(s =>
159
- s.platforms.includes(platform.toLowerCase())
160
- );
161
- }
162
- if (includePets) {
163
- // Priorizar estrategias que funcionan con mascotas
164
- strategies.sort((a, b) => {
165
- const aPet = a.name.toLowerCase().includes("pet") ? 1 : 0;
166
- const bPet = b.name.toLowerCase().includes("pet") ? 1 : 0;
167
- return bPet - aPet;
168
- });
169
- }
170
-
171
- // Generar ideas de contenido con IA para las tendencias principales
172
- const zai = await ZAI.create();
173
- const topTrends = trends.slice(0, 5);
174
-
175
- const ideasResponse = await zai.chat.completions.create({
176
- messages: [
177
- {
178
- role: "system",
179
- content: `Eres un experto en marketing de contenidos viral. Genera ideas de contenido basadas en tendencias.
180
- ${includePets ? "IMPORTANTE: Todas las ideas deben incluir una mascota como elemento central." : ""}
181
- Responde en JSON array con objetos {title, description, format, estimatedEngagement, viralScore, hook}.`
182
- },
183
- {
184
- role: "user",
185
- content: `Genera 5 ideas de contenido para estas tendencias: ${topTrends.map(t => t.name).join(", ")}
186
- Plataforma principal: ${platform || "todas"}
187
- ${includePets ? "Incluir mascota en todas las ideas." : ""}`
188
- }
189
- ],
190
- temperature: 0.8,
191
- });
192
-
193
- let contentIdeas = [];
194
- try {
195
- const match = ideasResponse.choices[0]?.message?.content?.match(/\[[\s\S]*\]/);
196
- if (match) {
197
- contentIdeas = JSON.parse(match[0]);
198
- }
199
- } catch {
200
- contentIdeas = [];
201
- }
202
-
203
- return NextResponse.json({
204
- success: true,
205
- trends,
206
- viralStrategies: strategies,
207
- contentIdeas,
208
- platform: platform || "all",
209
- stats: {
210
- totalTrends: trends.length,
211
- topGrowth: trends[0]?.growth || 0,
212
- avgGrowth: trends.reduce((acc, t) => acc + t.growth, 0) / trends.length || 0
213
- }
214
- });
215
-
216
- } catch (error) {
217
- console.error("Error fetching trends:", error);
218
- return NextResponse.json(
219
- { success: false, error: "Error al obtener tendencias" },
220
- { status: 500 }
221
- );
222
  }
223
  }
224
 
225
- // POST - Analizar tendencias con IA y generar plan viral
226
  export async function POST(request: NextRequest) {
227
  try {
228
  const body = await request.json();
229
- const { niche, platform, targetAudience, includePets, petType, daysToViral } = body;
230
 
231
  const zai = await ZAI.create();
232
-
233
  const completion = await zai.chat.completions.create({
234
  messages: [
235
- {
236
- role: "system",
237
- content: `Eres un analista de tendencias de redes sociales y experto en crear contenido viral. Analiza y predice tendencias para creadores de contenido.
238
- ${includePets ? "El creador tiene una mascota que quiere integrar en su contenido." : ""}
239
- Responde en JSON con esta estructura:
240
- {
241
- "currentTrends": [{"name", "type", "growth", "saturation"}],
242
- "emergingTrends": [{"name", "type", "potential"}],
243
- "contentGaps": [{"topic", "opportunity", "difficulty"}],
244
- "recommendations": ["rec1", "rec2"],
245
- "bestPostingTimes": ["time1", "time2"],
246
- "hashtagStrategy": {"primary": [], "secondary": []},
247
- "viralPlan": {
248
- "week1": [{"day", "contentType", "topic", "hook"}],
249
- "week2": [{"day", "contentType", "topic", "hook"}]
250
- },
251
- "petIntegration": ${includePets ? `[{"tip", "contentIdea", "engagementPotential"}]` : "null"},
252
- "predictedViralPotential": 0-100,
253
- "keySuccessFactors": []
254
- }`
255
- },
256
- {
257
- role: "user",
258
- content: `Analiza tendencias para:
259
- Nicho: ${niche || "general"}
260
- Plataforma: ${platform || "todas"}
261
- Audiencia: ${targetAudience || "general"}
262
- ${includePets ? `Incluir mascota tipo: ${petType || "perro/gato"}` : ""}
263
- Días objetivo para viralizar: ${daysToViral || 14}`
264
- }
265
- ],
266
- temperature: 0.7,
267
- max_tokens: 3000,
268
  });
269
 
270
- const response = completion.choices[0]?.message?.content || "";
271
- let analysis;
272
  try {
273
- const match = response.match(/\{[\s\S]*\}/);
274
- if (match) {
275
- analysis = JSON.parse(match[0]);
276
- }
277
- } catch {
278
- analysis = { raw: response };
279
- }
280
-
281
- // Guardar análisis en la base de datos
282
- await prisma.trend.create({
283
- data: {
284
- platform: platform || "all",
285
- type: "analysis",
286
- name: `Análisis ${niche || "general"} - ${new Date().toISOString().split('T')[0]}`,
287
- description: `Análisis de tendencias para nicho: ${niche}`,
288
- contentIdeas: JSON.stringify(analysis),
289
- isActive: true
290
- }
291
- }).catch(() => {
292
- // Ignorar errores de guardado
293
- });
294
 
295
- return NextResponse.json({
296
- success: true,
297
- analysis,
298
- timestamp: new Date().toISOString()
299
- });
300
 
301
- } catch (error) {
302
- console.error("Error analyzing trends:", error);
303
- return NextResponse.json(
304
- { success: false, error: "Error al analizar tendencias" },
305
- { status: 500 }
306
- );
307
  }
308
- }
309
-
310
- // PUT - Crear estrategia viral personalizada
311
- export async function PUT(request: NextRequest) {
312
- try {
313
- const body = await request.json();
314
- const { name, platform, contentType, hook, structure, elements, estimatedReach, timeframe } = body;
315
-
316
- const strategy = await prisma.viralStrategy.create({
317
- data: {
318
- name,
319
- platform,
320
- contentType,
321
- hook,
322
- structure: structure ? JSON.stringify(structure) : null,
323
- elements: elements ? JSON.stringify(elements) : null,
324
- estimatedReach,
325
- timeframe,
326
- isActive: true
327
- }
328
- });
329
-
330
- return NextResponse.json({
331
- success: true,
332
- strategy,
333
- message: "Estrategia viral creada correctamente"
334
- });
335
-
336
- } catch (error) {
337
- console.error("Error creating viral strategy:", error);
338
- return NextResponse.json(
339
- { success: false, error: "Error al crear estrategia" },
340
- { status: 500 }
341
- );
342
- }
343
- }
 
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 TRENDS = [
6
+ { platform: "Instagram", name: "#aesthetic", growth: 15.5, category: "lifestyle" },
7
+ { platform: "TikTok", name: "Storytime", growth: 32.1, category: "narrative" },
8
+ { platform: "YouTube", name: "Shorts", growth: 25.6, category: "video" },
9
+ { platform: "Instagram", name: "Pets content", growth: 55.8, category: "pets" }
10
+ ];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
 
12
  const VIRAL_STRATEGIES = [
13
+ { name: "Hook en 3 segundos", successRate: 85, platforms: ["tiktok", "instagram"] },
14
+ { name: "Storytime con suspenso", successRate: 78, platforms: ["tiktok"] },
15
+ { name: "Pet Reveal", successRate: 88, platforms: ["instagram", "tiktok"] }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  ];
17
 
 
18
  export async function GET(request: NextRequest) {
19
  try {
20
  const { searchParams } = new URL(request.url);
21
  const platform = searchParams.get("platform");
 
 
22
  const includePets = searchParams.get("includePets") === "true";
23
 
24
+ let trends = [...TRENDS];
25
+ if (platform) trends = trends.filter(t => t.platform.toLowerCase() === platform.toLowerCase());
26
+ if (includePets) trends = trends.filter(t => t.category === "pets");
 
 
 
 
 
 
 
 
 
 
 
 
27
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  trends.sort((a, b) => b.growth - a.growth);
29
 
30
+ return NextResponse.json({ success: true, trends, viralStrategies: VIRAL_STRATEGIES });
31
+ } catch {
32
+ return NextResponse.json({ success: false, error: "Error" }, { status: 500 });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  }
34
  }
35
 
 
36
  export async function POST(request: NextRequest) {
37
  try {
38
  const body = await request.json();
39
+ const { niche, platform } = body;
40
 
41
  const zai = await ZAI.create();
 
42
  const completion = await zai.chat.completions.create({
43
  messages: [
44
+ { role: "system", content: "Eres analista de tendencias. Responde JSON con {currentTrends:[], recommendations:[], predictedViralPotential:0-100}" },
45
+ { role: "user", content: "Analiza tendencias para nicho: " + (niche || "general") + " plataforma: " + (platform || "todas") }
46
+ ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  });
48
 
49
+ let analysis: Record<string, unknown> = {};
 
50
  try {
51
+ const match = completion.choices[0]?.message?.content?.match(/\{[\s\S]*\}/);
52
+ if (match) analysis = JSON.parse(match[0]);
53
+ } catch {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
 
55
+ await db.trend.create({
56
+ data: { platform: platform || "all", type: "analysis", name: "Analisis " + (niche || "general"), contentIdeas: JSON.stringify(analysis) }
57
+ }).catch(() => {});
 
 
58
 
59
+ return NextResponse.json({ success: true, analysis });
60
+ } catch {
61
+ return NextResponse.json({ success: false, error: "Error" }, { status: 500 });
 
 
 
62
  }
63
+ }