Skip to content

Commit bd28401

Browse files
committed
feat: Integrate Pusher for real-time chat functionality in Vertex Chat module, enhance environment configuration for broadcasting, and implement Google reCAPTCHA for improved security during user authentication.
1 parent 78824e0 commit bd28401

69 files changed

Lines changed: 2738 additions & 377 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ SESSION_PATH=/
3434
SESSION_DOMAIN=null
3535

3636
BROADCAST_CONNECTION=log
37+
# BROADCAST_CONNECTION=pusher # Use for Vertex Chat VIP real-time
38+
39+
# Pusher (Vertex Chat VIP - set when using real-time)
40+
# PUSHER_APP_ID=
41+
# PUSHER_APP_KEY=
42+
# PUSHER_APP_SECRET=
43+
# PUSHER_APP_CLUSTER=mt1
3744
FILESYSTEM_DISK=local
3845
QUEUE_CONNECTION=sync
3946

@@ -70,5 +77,9 @@ VITE_APP_NAME="${APP_NAME}"
7077
# TELESCOPE_ENABLED=false
7178
# PULSE_ENABLED=false
7279

80+
# Google reCAPTCHA v3 (Admin > Configurações > Segurança - grátis em https://www.google.com/recaptcha/admin)
81+
# RECAPTCHA_SITE_KEY=
82+
# RECAPTCHA_SECRET_KEY=
83+
7384
# Jules AI (Keep empty in example, set in .env or GitHub Secrets)
7485
JULES_API_KEY=

.env.testing

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ SESSION_PATH=/
3434
SESSION_DOMAIN=null
3535

3636
BROADCAST_CONNECTION=log
37+
# PUSHER_APP_ID= PUSHER_APP_KEY= PUSHER_APP_SECRET= PUSHER_APP_CLUSTER=mt1 # Vertex Chat VIP
3738
FILESYSTEM_DISK=local
3839
QUEUE_CONNECTION=sync
3940

AUDITORIA_PRODUCAO.md

Lines changed: 0 additions & 108 deletions
This file was deleted.

Modules/Core/app/Models/Inspection.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class Inspection extends Model
1010
{
1111
protected $fillable = [
1212
'ticket_id',
13+
'conversation_id',
1314
'agent_id',
1415
'user_id',
1516
'status',
@@ -30,6 +31,11 @@ public function ticket(): BelongsTo
3031
return $this->belongsTo(Ticket::class);
3132
}
3233

34+
public function conversation(): BelongsTo
35+
{
36+
return $this->belongsTo(\Modules\VertexChat\Models\Conversation::class);
37+
}
38+
3339
public function agent(): BelongsTo
3440
{
3541
return $this->belongsTo(User::class, 'agent_id');

Modules/Core/app/Providers/CoreServiceProvider.php

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Modules\Core\Providers;
44

5+
use Illuminate\Support\Facades\Auth;
56
use Illuminate\Support\Facades\Blade;
67
use Illuminate\Support\Facades\View;
78
use Illuminate\Support\ServiceProvider;
@@ -49,13 +50,27 @@ public function boot(): void
4950
// Share inspection sync flag: real user has active inspection (not agent) → enable real-time URL sync
5051
View::composer(['paneluser::components.layouts.master', 'paneluser::layouts.master'], function ($view) {
5152
$syncActive = false;
52-
if (auth()->check() && !session('original_agent_id')) {
53-
$syncActive = \Modules\Core\Models\Inspection::where('user_id', auth()->id())
53+
if (Auth::check() && !session('original_agent_id')) {
54+
$syncActive = \Modules\Core\Models\Inspection::where('user_id', Auth::id())
5455
->where('status', 'active')
5556
->exists();
5657
}
5758
$view->with('inspectionSyncActive', $syncActive);
5859
});
60+
61+
// Share branding variables for logo, favicon and panel names across all relevant views
62+
View::composer([
63+
'paneladmin::*',
64+
'paneluser::*',
65+
'panelsuporte::*',
66+
'homepage::*',
67+
'core::*',
68+
'errors.*',
69+
], function ($view) {
70+
$view->with('brandingLogoUrl', fn (string $context = 'default', bool $dark = false) => branding_logo_url($context, $dark));
71+
$view->with('brandingFaviconUrl', branding_favicon_url());
72+
$view->with('brandingPanelName', fn (string $panel) => branding_panel_name($panel));
73+
});
5974
}
6075

6176
/**
@@ -97,6 +112,26 @@ protected function overrideConfigsFromDatabase(): void
97112
'mail.from.address' => $settings->get('mail_from_address', env('MAIL_FROM_ADDRESS', 'hello@example.com')),
98113
'mail.from.name' => $settings->get('mail_from_name', env('MAIL_FROM_NAME', env('APP_NAME', 'Vertex Contas'))),
99114
]);
115+
116+
// Override Pusher/broadcasting configs (prioridade: DB > .env)
117+
$broadcastConn = $settings->get('broadcast_connection') ?? env('BROADCAST_CONNECTION', 'log');
118+
config(['broadcasting.default' => $broadcastConn]);
119+
120+
$pusherAppId = $settings->get('pusher_app_id') ?? env('PUSHER_APP_ID');
121+
if ($pusherAppId) {
122+
config([
123+
'broadcasting.connections.pusher.key' => $settings->get('pusher_app_key', env('PUSHER_APP_KEY')),
124+
'broadcasting.connections.pusher.secret' => $settings->get('pusher_app_secret', env('PUSHER_APP_SECRET')),
125+
'broadcasting.connections.pusher.app_id' => $pusherAppId,
126+
'broadcasting.connections.pusher.options.cluster' => $settings->get('pusher_app_cluster', env('PUSHER_APP_CLUSTER', 'mt1')),
127+
]);
128+
}
129+
130+
// Override session lifetime from security settings
131+
$sessionLifetime = (int) $settings->get('session_lifetime', config('session.lifetime', 120));
132+
if ($sessionLifetime > 0) {
133+
config(['session.lifetime' => $sessionLifetime]);
134+
}
100135
} catch (\Exception $e) {
101136
// Silently fail if database is not ready (e.g., during migrations)
102137
\Illuminate\Support\Facades\Log::debug('Settings override skipped: ' . $e->getMessage());
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Modules\Core\Services;
6+
7+
use Illuminate\Support\Facades\Http;
8+
9+
class RecaptchaService
10+
{
11+
protected const VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify';
12+
13+
public function __construct(
14+
protected SettingService $settingService
15+
) {}
16+
17+
public function isEnabled(): bool
18+
{
19+
return (bool) $this->settingService->get('recaptcha_enabled', false);
20+
}
21+
22+
public function getSiteKey(): ?string
23+
{
24+
$key = $this->settingService->get('recaptcha_site_key');
25+
26+
return $key ? (string) $key : null;
27+
}
28+
29+
public function verify(string $token, ?string $action = 'login'): bool
30+
{
31+
$secret = $this->settingService->get('recaptcha_secret_key');
32+
if (! $secret) {
33+
return false;
34+
}
35+
36+
$response = Http::asForm()->post(self::VERIFY_URL, [
37+
'secret' => $secret,
38+
'response' => $token,
39+
]);
40+
41+
if (! $response->successful()) {
42+
return false;
43+
}
44+
45+
$data = $response->json();
46+
if (! ($data['success'] ?? false)) {
47+
return false;
48+
}
49+
50+
$minScore = (float) ($this->settingService->get('recaptcha_min_score', 0.5));
51+
$score = (float) ($data['score'] ?? 0);
52+
53+
return $score >= $minScore;
54+
}
55+
}

Modules/Core/app/Services/TemplateDocumentService.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,17 +81,18 @@ public function getTemplateData(): array
8181
{
8282
$settings = $this->settingService->getByGroup('document_templates');
8383
$general = $this->settingService->getByGroup('general');
84-
$branding = $this->settingService->getByGroup('branding');
84+
85+
$companyName = $settings->get('company_name') ?? $general->get('app_name') ?? config('app.name', 'Vertex Contas');
8586

8687
return [
87-
'company_name' => $settings->get('company_name') ?? $general->get('app_name') ?? 'Vertex Contas',
88+
'company_name' => $companyName,
8889
'company_address' => $settings->get('company_address') ?? '',
8990
'company_cnpj' => $settings->get('company_cnpj') ?? '',
9091
'company_phone' => $settings->get('company_phone') ?? '',
9192
'company_email' => $settings->get('company_email') ?? '',
92-
'document_footer_text' => $settings->get('document_footer_text') ?? 'Vertex Contas - Sistema de Gestão Financeira',
93+
'document_footer_text' => $settings->get('document_footer_text') ?? $companyName . ' - Sistema de Gestão Financeira',
9394
'app_url' => $general->get('app_url') ?? config('app.url'),
94-
'logo_path' => $branding->get('app_logo') ? asset($branding->get('app_logo')) : asset('storage/logos/logo.svg'),
95+
'logo_path' => branding_logo_url('default', false),
9596
];
9697
}
9798

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
* Makes inspections support Ticket OR Chat flow.
12+
*/
13+
public function up(): void
14+
{
15+
Schema::table('inspections', function (Blueprint $table) {
16+
$table->foreignId('ticket_id')->nullable()->change();
17+
$table->foreignId('conversation_id')->nullable()->after('ticket_id')
18+
->constrained('vertex_chat_conversations')->onDelete('cascade');
19+
});
20+
}
21+
22+
/**
23+
* Reverse the migrations.
24+
*/
25+
public function down(): void
26+
{
27+
Schema::table('inspections', function (Blueprint $table) {
28+
$table->dropForeign(['conversation_id']);
29+
$table->foreignId('ticket_id')->nullable(false)->change();
30+
});
31+
}
32+
};

0 commit comments

Comments
 (0)