Spaces:
Build error
Build error
| import { NextRequest, NextResponse } from "next/server"; | |
| import { db } from "@/lib/db"; | |
| import ZAI from "z-ai-web-dev-sdk"; | |
| // GET - Listar publicaciones | |
| export async function GET(request: NextRequest) { | |
| try { | |
| const { searchParams } = new URL(request.url); | |
| const status = searchParams.get("status"); | |
| const platform = searchParams.get("platform"); | |
| const type = searchParams.get("type"); | |
| const limit = parseInt(searchParams.get("limit") || "50"); | |
| const where: Record<string, unknown> = {}; | |
| if (status) where.status = status; | |
| if (type) where.type = type; | |
| if (platform) where.platformId = platform; | |
| const posts = await db.post.findMany({ | |
| where, | |
| include: { | |
| content: true, | |
| platform: true, | |
| story: true, | |
| }, | |
| orderBy: { scheduledAt: "asc" }, | |
| take: limit, | |
| }); | |
| // Estad铆sticas | |
| const stats = { | |
| total: await db.post.count(), | |
| draft: await db.post.count({ where: { status: "draft" } }), | |
| scheduled: await db.post.count({ where: { status: "scheduled" } }), | |
| published: await db.post.count({ where: { status: "published" } }), | |
| failed: await db.post.count({ where: { status: "failed" } }), | |
| }; | |
| // Pr贸ximas publicaciones | |
| const upcoming = await db.post.findMany({ | |
| where: { | |
| status: "scheduled", | |
| scheduledAt: { gte: new Date() } | |
| }, | |
| orderBy: { scheduledAt: "asc" }, | |
| take: 5, | |
| }); | |
| return NextResponse.json({ | |
| success: true, | |
| posts, | |
| stats, | |
| upcoming | |
| }); | |
| } catch (error) { | |
| console.error("Error fetching posts:", error); | |
| return NextResponse.json( | |
| { success: false, error: "Error al obtener publicaciones" }, | |
| { status: 500 } | |
| ); | |
| } | |
| } | |
| // POST - Crear nueva publicaci贸n | |
| export async function POST(request: NextRequest) { | |
| try { | |
| const body = await request.json(); | |
| const { | |
| title, | |
| caption, | |
| type, // reel, photo, carousel, story, post | |
| contentId, | |
| platformId, | |
| scheduledAt, | |
| hashtags, | |
| storyId, | |
| autoGenerateCaption, | |
| optimizeForPlatform, | |
| } = body; | |
| if (!type) { | |
| return NextResponse.json( | |
| { success: false, error: "El tipo de publicaci贸n es requerido" }, | |
| { status: 400 } | |
| ); | |
| } | |
| let finalCaption = caption; | |
| let finalHashtags = hashtags; | |
| // Generar caption con IA si se solicita | |
| if (autoGenerateCaption && contentId) { | |
| const content = await db.content.findUnique({ | |
| where: { id: contentId } | |
| }); | |
| if (content) { | |
| const zai = await ZAI.create(); | |
| const platform = await db.monetizationPlatform.findUnique({ | |
| where: { id: platformId } | |
| }); | |
| const platformName = platform?.name || "general"; | |
| const platformRules = PLATFORM_RULES[platformName.toLowerCase()] || {}; | |
| const completion = await zai.chat.completions.create({ | |
| messages: [ | |
| { | |
| role: "system", | |
| content: `Eres un experto en marketing de contenidos para ${platformName}. | |
| Genera captions atractivos que: | |
| ${platformRules.maxCaptionLength ? `- No excedan ${platformRules.maxCaptionLength} caracteres` : ''} | |
| - Incluyan emojis relevantes | |
| - Tengan un CTA (call to action) efectivo | |
| - Sean ${platformRules.tone || 'profesionales pero cercanos'} | |
| - Maximizen el engagement | |
| ${platformRules.hashtagLimit ? `- Incluyan m谩ximo ${platformRules.hashtagLimit} hashtags relevantes` : ''}` | |
| }, | |
| { | |
| role: "user", | |
| content: `Genera un caption para este contenido: | |
| Tipo: ${type} | |
| T铆tulo: ${title || content.title} | |
| Descripci贸n: ${content.description || content.prompt} | |
| Plataforma: ${platformName}` | |
| } | |
| ], | |
| temperature: 0.8, | |
| }); | |
| finalCaption = completion.choices[0]?.message?.content || caption; | |
| } | |
| } | |
| // Optimizar hashtags si se solicita | |
| if (optimizeForPlatform && !hashtags) { | |
| const zai = await ZAI.create(); | |
| const platform = await db.monetizationPlatform.findUnique({ | |
| where: { id: platformId } | |
| }); | |
| const completion = await zai.chat.completions.create({ | |
| messages: [ | |
| { | |
| role: "system", | |
| content: `Eres un experto en SEO de redes sociales. Genera hashtags optimizados. | |
| Responde SOLO con un JSON array de hashtags sin el s铆mbolo #. | |
| Ejemplo: ["tendencias", "viral", "lifestyle"]` | |
| }, | |
| { | |
| role: "user", | |
| content: `Genera hashtags para un ${type} en ${platform?.name || "redes sociales"}. | |
| Tema: ${title || "contenido general"} | |
| M谩ximo 10 hashtags.` | |
| } | |
| ], | |
| temperature: 0.7, | |
| }); | |
| try { | |
| const response = completion.choices[0]?.message?.content || "[]"; | |
| const match = response.match(/\[[\s\S]*\]/); | |
| if (match) { | |
| finalHashtags = match[0]; | |
| } | |
| } catch { | |
| finalHashtags = "[]"; | |
| } | |
| } | |
| // Crear publicaci贸n | |
| const post = await db.post.create({ | |
| data: { | |
| title: title || null, | |
| caption: finalCaption, | |
| hashtags: finalHashtags, | |
| type, | |
| status: scheduledAt ? "scheduled" : "draft", | |
| contentId: contentId || null, | |
| platformId: platformId || null, | |
| scheduledAt: scheduledAt ? new Date(scheduledAt) : null, | |
| storyId: storyId || null, | |
| }, | |
| include: { | |
| content: true, | |
| platform: true, | |
| } | |
| }); | |
| // Crear tarea de agente | |
| await db.agentTask.create({ | |
| data: { | |
| type: "create_post", | |
| status: "completed", | |
| input: `Crear ${type}: ${title || "sin t铆tulo"}`, | |
| output: `Post creado con ID: ${post.id}`, | |
| completedAt: new Date(), | |
| } | |
| }); | |
| return NextResponse.json({ | |
| success: true, | |
| post, | |
| message: scheduledAt | |
| ? `Publicaci贸n programada para ${new Date(scheduledAt).toLocaleString()}` | |
| : "Publicaci贸n creada como borrador" | |
| }); | |
| } catch (error) { | |
| console.error("Error creating post:", error); | |
| return NextResponse.json( | |
| { success: false, error: "Error al crear publicaci贸n" }, | |
| { status: 500 } | |
| ); | |
| } | |
| } | |
| // PUT - Actualizar publicaci贸n | |
| export async function PUT(request: NextRequest) { | |
| try { | |
| const body = await request.json(); | |
| const { id, status, scheduledAt, caption, hashtags, publishedAt, engagementStats } = body; | |
| if (!id) { | |
| return NextResponse.json( | |
| { success: false, error: "ID requerido" }, | |
| { status: 400 } | |
| ); | |
| } | |
| const updateData: Record<string, unknown> = {}; | |
| if (status) updateData.status = status; | |
| if (scheduledAt) updateData.scheduledAt = new Date(scheduledAt); | |
| if (caption) updateData.caption = caption; | |
| if (hashtags) updateData.hashtags = hashtags; | |
| if (publishedAt) updateData.publishedAt = new Date(publishedAt); | |
| if (engagementStats) updateData.engagementStats = engagementStats; | |
| const post = await db.post.update({ | |
| where: { id }, | |
| data: updateData, | |
| }); | |
| return NextResponse.json({ | |
| success: true, | |
| post | |
| }); | |
| } catch (error) { | |
| console.error("Error updating post:", error); | |
| return NextResponse.json( | |
| { success: false, error: "Error al actualizar publicaci贸n" }, | |
| { status: 500 } | |
| ); | |
| } | |
| } | |
| // DELETE - Eliminar publicaci贸n | |
| export async function DELETE(request: NextRequest) { | |
| try { | |
| const { searchParams } = new URL(request.url); | |
| const id = searchParams.get("id"); | |
| if (!id) { | |
| return NextResponse.json( | |
| { success: false, error: "ID requerido" }, | |
| { status: 400 } | |
| ); | |
| } | |
| await db.post.delete({ | |
| where: { id } | |
| }); | |
| return NextResponse.json({ | |
| success: true, | |
| message: "Publicaci贸n eliminada" | |
| }); | |
| } catch (error) { | |
| console.error("Error deleting post:", error); | |
| return NextResponse.json( | |
| { success: false, error: "Error al eliminar publicaci贸n" }, | |
| { status: 500 } | |
| ); | |
| } | |
| } | |
| // Reglas espec铆ficas por plataforma | |
| const PLATFORM_RULES: Record<string, { | |
| maxCaptionLength?: number; | |
| hashtagLimit?: number; | |
| tone?: string; | |
| bestPostingTimes?: string[]; | |
| }> = { | |
| instagram: { | |
| maxCaptionLength: 2200, | |
| hashtagLimit: 30, | |
| tone: "inspirador y visual", | |
| bestPostingTimes: ["11:00", "14:00", "19:00", "21:00"] | |
| }, | |
| tiktok: { | |
| maxCaptionLength: 300, | |
| hashtagLimit: 5, | |
| tone: "casual y divertido", | |
| bestPostingTimes: ["09:00", "12:00", "19:00"] | |
| }, | |
| youtube: { | |
| maxCaptionLength: 5000, | |
| tone: "profesional e informativo", | |
| bestPostingTimes: ["15:00", "16:00", "17:00"] | |
| }, | |
| onlyfans: { | |
| maxCaptionLength: 1000, | |
| tone: "personal y exclusivo", | |
| bestPostingTimes: ["10:00", "18:00", "22:00"] | |
| }, | |
| patreon: { | |
| maxCaptionLength: 5000, | |
| tone: "profesional y cercano", | |
| bestPostingTimes: ["10:00", "14:00", "18:00"] | |
| }, | |
| twitter: { | |
| maxCaptionLength: 280, | |
| hashtagLimit: 3, | |
| tone: "conciso y directo", | |
| bestPostingTimes: ["09:00", "12:00", "17:00", "20:00"] | |
| } | |
| }; | |