UNPKG

sysrot-hub

Version:

CLI de nueva generación para proyectos Next.js 14+ con IA multi-modelo, Web3 integration, internacionalización completa y roadmap realista 2025-2026

398 lines (356 loc) 11.2 kB
import { NextApiRequest, NextApiResponse } from 'next'; import { PrismaClient } from '@prisma/client'; import { getSession } from 'next-auth/react'; import { z } from 'zod'; const prisma = new PrismaClient(); // Validation schemas const createChatbotSchema = z.object({ name: z.string().min(1, 'Chatbot name is required').max(100, 'Name too long'), description: z.string().max(500, 'Description too long').optional(), avatar: z.string().url().optional(), // AI Configuration model: z.enum(['gpt-4o', 'claude-3-5-sonnet', 'gemini-flash-pro', 'deepseek-r1']).default('gpt-4o'), temperature: z.number().min(0).max(2).default(0.7), maxTokens: z.number().min(50).max(4000).default(1000), systemPrompt: z.string().min(10, 'System prompt is required').max(2000, 'System prompt too long'), // Behavior settings personality: z.record(z.any()).optional(), capabilities: z.array(z.string()).default([]), restrictions: z.array(z.string()).default([]), languages: z.array(z.string()).default(['en']), // Integration settings isPublic: z.boolean().default(false), isEmbeddable: z.boolean().default(true), webhookUrl: z.string().url().optional(), fallbackMessage: z.string().optional(), // Business settings businessHours: z.record(z.any()).optional(), autoAssign: z.boolean().default(false), maxConversations: z.number().min(1).max(1000).optional(), organizationId: z.string().optional() }); const updateChatbotSchema = createChatbotSchema.partial(); async function hasPermission(userId: string, organizationId?: string, action: string = 'read') { if (!organizationId) return true; // Public chatbots const membership = await prisma.organizationMember.findUnique({ where: { organizationId_userId: { organizationId, userId } } }); if (!membership) return false; // Permission logic based on role and action switch (action) { case 'create': case 'update': case 'delete': return ['OWNER', 'ADMIN'].includes(membership.role); case 'read': return true; // All members can read default: return false; } } export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { const session = await getSession({ req }); if (!session?.user) { return res.status(401).json({ error: 'Authentication required' }); } const { method } = req; switch (method) { case 'GET': return await getChatbots(req, res); case 'POST': return await createChatbot(req, res); default: res.setHeader('Allow', ['GET', 'POST']); return res.status(405).end('Method Not Allowed'); } } catch (error) { console.error('Chatbots API error:', error); return res.status(500).json({ error: 'Internal server error' }); } finally { await prisma.$disconnect(); } } async function getChatbots(req: NextApiRequest, res: NextApiResponse) { const session = await getSession({ req }); const { organizationId, includePublic = 'false', limit = '20', page = '1' } = req.query; try { const pageNum = parseInt(page as string); const limitNum = parseInt(limit as string); const offset = (pageNum - 1) * limitNum; // Build where clause const whereClause: any = { OR: [] }; // User's own chatbots whereClause.OR.push({ createdBy: session.user.id }); // Organization chatbots if user has access if (organizationId && typeof organizationId === 'string') { const hasAccess = await hasPermission(session.user.id, organizationId, 'read'); if (hasAccess) { whereClause.OR.push({ organizationId }); } } // Public chatbots if requested if (includePublic === 'true') { whereClause.OR.push({ isPublic: true, isActive: true }); } // Get chatbots with analytics const chatbots = await prisma.chatbot.findMany({ where: whereClause, include: { creator: { select: { id: true, name: true, email: true, image: true } }, organization: { select: { id: true, name: true, slug: true } }, _count: { select: { conversations: true, knowledgeItems: true, intents: true } }, analytics: { where: { date: { gte: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) // Last 30 days }, period: 'DAY' }, orderBy: { date: 'desc' }, take: 7 // Last 7 days } }, orderBy: [ { isActive: 'desc' }, { updatedAt: 'desc' } ], skip: offset, take: limitNum }); // Calculate summary stats for each chatbot const enrichedChatbots = chatbots.map(chatbot => { const recentAnalytics = chatbot.analytics; const totalConversations = recentAnalytics.reduce((sum, a) => sum + a.conversationsStarted, 0); const totalMessages = recentAnalytics.reduce((sum, a) => sum + a.messagesReceived, 0); const avgRating = recentAnalytics.length > 0 ? recentAnalytics.reduce((sum, a) => sum + a.averageRating, 0) / recentAnalytics.length : 0; return { ...chatbot, analytics: undefined, // Remove from response stats: { totalConversations, totalMessages, averageRating: avgRating, knowledgeItems: chatbot._count.knowledgeItems, intents: chatbot._count.intents, isTraining: chatbot.isTraining } }; }); // Get total count for pagination const total = await prisma.chatbot.count({ where: whereClause }); res.status(200).json({ chatbots: enrichedChatbots, pagination: { page: pageNum, limit: limitNum, total, pages: Math.ceil(total / limitNum) } }); } catch (error) { console.error('Error fetching chatbots:', error); res.status(500).json({ error: 'Failed to fetch chatbots' }); } } async function createChatbot(req: NextApiRequest, res: NextApiResponse) { const session = await getSession({ req }); try { const validatedData = createChatbotSchema.parse(req.body); // Check organization permissions if specified if (validatedData.organizationId) { const hasAccess = await hasPermission(session.user.id, validatedData.organizationId, 'create'); if (!hasAccess) { return res.status(403).json({ error: 'Insufficient permissions' }); } } // Check if user has reached chatbot limits const userChatbotCount = await prisma.chatbot.count({ where: { createdBy: session.user.id, isActive: true } }); const maxChatbots = validatedData.organizationId ? 50 : 5; // Higher limit for organizations if (userChatbotCount >= maxChatbots) { return res.status(400).json({ error: `Maximum chatbot limit reached (${maxChatbots})` }); } // Create the chatbot const chatbot = await prisma.chatbot.create({ data: { ...validatedData, createdBy: session.user.id }, include: { creator: { select: { id: true, name: true, email: true, image: true } }, organization: { select: { id: true, name: true, slug: true } } } }); // Create default intents and responses await createDefaultIntents(chatbot.id); // Initialize analytics await prisma.botAnalytics.create({ data: { chatbotId: chatbot.id, date: new Date(), period: 'DAY' } }); res.status(201).json({ chatbot, message: 'Chatbot created successfully' }); } catch (error) { if (error instanceof z.ZodError) { return res.status(400).json({ error: 'Validation failed', details: error.errors }); } console.error('Error creating chatbot:', error); res.status(500).json({ error: 'Failed to create chatbot' }); } } // Helper function to create default intents async function createDefaultIntents(chatbotId: string) { const defaultIntents = [ { name: 'greeting', description: 'User greetings and hello messages', examples: [ 'hello', 'hi', 'hey there', 'good morning', 'good afternoon', 'greetings' ], responses: [ 'Hello! How can I help you today?', 'Hi there! What can I do for you?', 'Greetings! I\'m here to assist you.' ] }, { name: 'goodbye', description: 'User farewells and goodbye messages', examples: [ 'bye', 'goodbye', 'see you later', 'farewell', 'talk to you later', 'have a good day' ], responses: [ 'Goodbye! Have a great day!', 'See you later! Feel free to come back anytime.', 'Take care! I\'ll be here if you need anything.' ] }, { name: 'help', description: 'User requests for help or assistance', examples: [ 'help', 'i need help', 'can you help me', 'what can you do', 'how does this work', 'assistance needed' ], responses: [ 'I\'m here to help! You can ask me questions and I\'ll do my best to assist you.', 'Of course! What would you like help with?', 'I\'d be happy to help. What do you need assistance with?' ] }, { name: 'unknown', description: 'Fallback for unrecognized inputs', examples: [], responses: [ 'I\'m not sure I understand. Could you please rephrase that?', 'I didn\'t quite get that. Can you ask in a different way?', 'I\'m still learning! Could you clarify what you mean?' ] } ]; for (const intentData of defaultIntents) { // Create intent const intent = await prisma.botIntent.create({ data: { chatbotId, name: intentData.name, description: intentData.description, examples: intentData.examples, patterns: [] } }); // Create responses for this intent for (const responseText of intentData.responses) { await prisma.botResponse.create({ data: { chatbotId, intentId: intent.id, content: responseText, contentType: 'TEXT' } }); } } }