Files
boursecu_server/app/Services/ChatWebhookService.php
T

156 lines
5.0 KiB
PHP
Raw Normal View History

2026-08-04 18:59:38 +03:30
<?php
namespace App\Services;
use App\Http\Controllers\Controller;
use App\Models\Setting;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
class ChatWebhookService
{
public function ensureRegistered(): bool
{
$chatApi = Controller::Config('chat_api');
$chatApiKey = Controller::Config('chat_api_key');
if (!$chatApi || !$chatApiKey) {
return false;
}
$host = preg_replace('#^https?://#', '', rtrim((string) $chatApi, '/'));
$secret = $this->ensureSecret();
if ($host === '' || $secret === '') {
return false;
}
2026-08-05 12:56:52 +03:30
$webhookUrl = $this->webhookUrl();
if ($this->isUnreachableWebhookUrl($webhookUrl)) {
Log::warning('chat webhook register refused: unreachable/local URL', [
'webhook_url' => $webhookUrl,
'hint' => 'Set chat_webhook_base_url to public Laravel URL (e.g. https://niazitala.ir/server) or fix APP_URL',
]);
return false;
}
2026-08-04 18:59:38 +03:30
try {
$response = Http::timeout(8)->post('https://'.$host.'/api/projects/set-webhook', [
'api_key' => $chatApiKey,
'webhook_url' => $webhookUrl,
'webhook_secret' => $secret,
]);
2026-08-05 12:56:52 +03:30
if (! $response->successful()) {
Log::warning('chat webhook register non-2xx', [
'host' => $host,
'webhook_url' => $webhookUrl,
'status' => $response->status(),
'body' => mb_substr($response->body(), 0, 300),
]);
return false;
}
Log::info('chat webhook registered', [
'host' => $host,
'webhook_url' => $webhookUrl,
]);
return true;
2026-08-04 18:59:38 +03:30
} catch (\Throwable $e) {
Log::warning('chat webhook register failed', [
'host' => $host,
2026-08-05 12:56:52 +03:30
'webhook_url' => $webhookUrl,
2026-08-04 18:59:38 +03:30
'error' => $e->getMessage(),
]);
return false;
}
}
2026-08-05 12:56:52 +03:30
/**
* آدرس عمومی وب‌هوک — باید به لاراول بخورد نه فرانت SPA.
* اولویت: chat_webhook_base_url (مثلاً https://niazitala.ir/server) سپس APP_URL.
*/
public function webhookUrl(): string
{
$base = trim((string) Controller::Config('chat_webhook_base_url'));
if ($base === '' || $base === '0' || strtolower($base) === 'false') {
$base = rtrim((string) config('app.url'), '/');
} else {
$base = rtrim($base, '/');
}
return $base.'/api/chat/support-message-notify';
}
public function isUnreachableWebhookUrl(string $url): bool
{
$host = parse_url($url, PHP_URL_HOST);
if (! is_string($host) || $host === '') {
return true;
}
$host = strtolower($host);
return in_array($host, ['localhost', '127.0.0.1', '::1', '0.0.0.0'], true)
|| str_ends_with($host, '.local')
|| str_ends_with($host, '.internal');
}
2026-08-04 18:59:38 +03:30
public function ensureSecret(): string
{
2026-08-05 12:56:52 +03:30
$existing = $this->sanitizeSecret((string) Controller::Config('chat_webhook_secret'));
2026-08-04 18:59:38 +03:30
if ($existing !== '') {
return $existing;
}
$secret = Str::random(48);
$row = Setting::where('key', 'chat_webhook_secret')->first();
if ($row) {
2026-08-05 12:56:52 +03:30
$current = $this->sanitizeSecret((string) $row->value);
if ($current === '') {
2026-08-04 18:59:38 +03:30
$row->value = $secret;
$row->save();
} else {
2026-08-05 12:56:52 +03:30
// مقدار ذخیره‌شده کاراکتر نامعتبر هدر داشت — با نسخهٔ تمیز جایگزین کن
if ($current !== trim((string) $row->value)) {
$row->value = $current;
$row->save();
}
return $current;
2026-08-04 18:59:38 +03:30
}
} else {
Setting::create([
'key' => 'chat_webhook_secret',
'value' => $secret,
'title_fa' => 'رمز وب‌هوک چت',
'is_active' => true,
'can_edit' => 0,
'description' => 'رمز مشترک بین لاراول و سرور چت برای اعلان پیام پشتیبانی. فقط سرور؛ در صورت خالی بودن به‌صورت خودکار ساخته می‌شود.',
'type' => 'text',
]);
}
return $secret;
}
2026-08-05 12:56:52 +03:30
/** فقط ASCII قابل‌استفاده در هدر HTTP (بدون فاصله/خط جدید/یونیکد). */
public function sanitizeSecret(string $secret): string
{
$secret = trim($secret);
if ($secret === '') {
return '';
}
// حذف کاراکترهای غیرقابل‌قبول برای header value
$clean = preg_replace('/[^\x21-\x7E]/', '', $secret) ?? '';
return trim($clean);
}
2026-08-04 18:59:38 +03:30
}