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:
marco
2026-09-07 19:06:53 -03:00
commit 38295e7e88
279 changed files with 33691 additions and 0 deletions
@@ -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,
]);
}
}