feat(cms): implement complete SysSiteCMS content management system
Initial implementation of the HomeSysPanel CMS built with Laravel 13.0. Includes complete admin panel, content management, user authentication, modular architecture for products/services/chatbot, and marketing tools. Key features: - User authentication with email verification and password reset - Role-based access control (Admin/User with granular permissions) - Content management (pages, news, FAQ, downloads, galleries, banners) - Contact management system with subjects and responses - Marketing tools (email campaigns, visitor analytics, social sharing) - Modular architecture (Products, Services, Chatbot modules) - AI Chatbot integration with OpenRouter.ai and LlamaCpp support - Visitor tracking with geolocation - SEO optimization with meta tags and Open Graph - Responsive design with Bootstrap 5 - Dark mode support - Maintenance mode - Feature request system
This commit is contained in:
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
|
||||
namespace App\Modules\Chatbot\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Modules\Chatbot\Models\ChatbotConversation;
|
||||
use App\Modules\Chatbot\Models\ChatbotMessage;
|
||||
use App\Modules\Chatbot\Notifications\ChatbotConversationNotification;
|
||||
use App\Modules\Chatbot\Services\OpenRouterService;
|
||||
use App\Modules\Chatbot\Services\LlamaCppService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class ChatbotApiController extends Controller
|
||||
{
|
||||
/**
|
||||
* Register visitor information before starting the chat.
|
||||
*/
|
||||
public function registerVisitor(Request $request): JsonResponse
|
||||
{
|
||||
// Check if chatbot is enabled
|
||||
if (Setting::get('chatbot_enabled', 'off') !== 'on') {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'O assistente virtual está indisponível no momento.',
|
||||
], 403);
|
||||
}
|
||||
|
||||
$validator = Validator::make($request->all(), [
|
||||
'session_id' => 'required|string|max:255|regex:/^[a-zA-Z0-9\-_]+$/',
|
||||
'name' => 'required|string|max:255',
|
||||
'phone' => 'required|string|max:30',
|
||||
'city' => 'required|string|max:255',
|
||||
], [
|
||||
'session_id.regex' => 'ID de sessão inválido.',
|
||||
'name.required' => 'Por favor, informe seu nome.',
|
||||
'phone.required' => 'Por favor, informe seu telefone.',
|
||||
'city.required' => 'Por favor, informe sua cidade.',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Preencha todos os campos obrigatórios.',
|
||||
'errors' => $validator->errors(),
|
||||
], 422);
|
||||
}
|
||||
|
||||
$sessionId = $request->input('session_id');
|
||||
|
||||
// Get or create conversation and save visitor info
|
||||
$conversation = ChatbotConversation::getOrCreateBySession($sessionId);
|
||||
$conversation->update([
|
||||
'visitor_name' => trim($request->input('name')),
|
||||
'visitor_phone' => trim($request->input('phone')),
|
||||
'visitor_city' => trim($request->input('city')),
|
||||
]);
|
||||
|
||||
// Send notification to admins
|
||||
$adminTypes = \App\Models\UserType::where('is_admin', true)->pluck('name')->toArray();
|
||||
$admins = User::whereIn('user_type', $adminTypes)->get();
|
||||
|
||||
if ($admins->isNotEmpty()) {
|
||||
Notification::send($admins, new ChatbotConversationNotification($conversation));
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Dados registrados com sucesso.',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming chat message from the public widget.
|
||||
*/
|
||||
public function sendMessage(Request $request): JsonResponse
|
||||
{
|
||||
// Check if chatbot is enabled
|
||||
if (Setting::get('chatbot_enabled', 'off') !== 'on') {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'O assistente virtual está indisponível no momento.',
|
||||
], 403);
|
||||
}
|
||||
|
||||
$validator = Validator::make($request->all(), [
|
||||
'message' => 'required|string|max:1000',
|
||||
'session_id' => 'required|string|max:255|regex:/^[a-zA-Z0-9\-_]+$/',
|
||||
], [
|
||||
'session_id.regex' => 'ID de sessão inválido.',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Mensagem inválida.',
|
||||
'errors' => $validator->errors(),
|
||||
], 422);
|
||||
}
|
||||
|
||||
$sessionId = $request->input('session_id');
|
||||
$userMessage = trim($request->input('message'));
|
||||
|
||||
// Get or create conversation
|
||||
$conversation = ChatbotConversation::getOrCreateBySession($sessionId);
|
||||
|
||||
// Check if conversation is ended
|
||||
if ($conversation->isEnded()) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Esta conversa foi finalizada. Inicie uma nova conversa.',
|
||||
'conversation_ended' => true,
|
||||
], 403);
|
||||
}
|
||||
|
||||
// Save user message
|
||||
ChatbotMessage::create([
|
||||
'conversation_id' => $conversation->id,
|
||||
'role' => 'user',
|
||||
'content' => $userMessage,
|
||||
]);
|
||||
|
||||
// Get conversation context
|
||||
$context = $conversation->getContextForApi(20);
|
||||
|
||||
// Call the configured AI provider (OpenRouter or llama.cpp)
|
||||
$provider = Setting::get('chatbot_provider', 'openrouter');
|
||||
|
||||
if ($provider === 'llamacpp') {
|
||||
$aiService = new LlamaCppService();
|
||||
} else {
|
||||
$aiService = new OpenRouterService();
|
||||
}
|
||||
|
||||
$result = $aiService->chat($context, $conversation);
|
||||
|
||||
// Save assistant message with token usage
|
||||
ChatbotMessage::create([
|
||||
'conversation_id' => $conversation->id,
|
||||
'role' => 'assistant',
|
||||
'content' => $result['content'],
|
||||
'tokens_used' => $result['tokens_used'],
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => $result['content'],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* End a conversation.
|
||||
*/
|
||||
public function endConversation(Request $request): JsonResponse
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
'session_id' => 'required|string|max:255|regex:/^[a-zA-Z0-9\-_]+$/',
|
||||
], [
|
||||
'session_id.regex' => 'ID de sessão inválido.',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Sessão inválida.',
|
||||
], 422);
|
||||
}
|
||||
|
||||
$sessionId = $request->input('session_id');
|
||||
$conversation = ChatbotConversation::where('session_id', $sessionId)->first();
|
||||
|
||||
if (!$conversation) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Conversa não encontrada.',
|
||||
], 404);
|
||||
}
|
||||
|
||||
if ($conversation->isEnded()) {
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Conversa já foi finalizada.',
|
||||
]);
|
||||
}
|
||||
|
||||
$conversation->update([
|
||||
'status' => 'ended',
|
||||
'ended_at' => now(),
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Conversa finalizada com sucesso.',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit a rating for a conversation.
|
||||
*/
|
||||
public function rateConversation(Request $request): JsonResponse
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
'session_id' => 'required|string|max:255|regex:/^[a-zA-Z0-9\-_]+$/',
|
||||
'rating' => 'required|integer|min:1|max:5',
|
||||
'comment' => 'nullable|string|max:500',
|
||||
], [
|
||||
'session_id.regex' => 'ID de sessão inválido.',
|
||||
'rating.required' => 'Por favor, selecione uma avaliação.',
|
||||
'rating.min' => 'A avaliação mínima é 1 estrela.',
|
||||
'rating.max' => 'A avaliação máxima é 5 estrelas.',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Avaliação inválida.',
|
||||
'errors' => $validator->errors(),
|
||||
], 422);
|
||||
}
|
||||
|
||||
$sessionId = $request->input('session_id');
|
||||
$conversation = ChatbotConversation::where('session_id', $sessionId)->first();
|
||||
|
||||
if (!$conversation) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Conversa não encontrada.',
|
||||
], 404);
|
||||
}
|
||||
|
||||
$conversation->update([
|
||||
'rating' => $request->input('rating'),
|
||||
'rating_comment' => $request->input('comment'),
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Avaliação registrada. Obrigado pelo feedback!',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the initial welcome message and chatbot status.
|
||||
*/
|
||||
public function getStatus(): JsonResponse
|
||||
{
|
||||
$enabled = Setting::get('chatbot_enabled', 'off') === 'on';
|
||||
$welcomeMessage = Setting::get('chatbot_welcome_message', 'Olá! Como posso ajudar você hoje?');
|
||||
|
||||
return response()->json([
|
||||
'enabled' => $enabled,
|
||||
'welcome_message' => $welcomeMessage,
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user