sofia-cloud / src /app /api /posts /route.ts
Gmagl
Add Sofia Cloud complete files
333c51a
Raw
History Blame
9.3 kB
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"]
}
};