- Phase 1: backup.conf.example + sam-db-backup.sh 백업 스크립트 - Phase 2: BackupCheckCommand + StatMonitorService.recordBackupFailure() - Phase 2: routes/console.php에 db:backup-check 05:00 스케줄 등록 - Phase 4: SlackNotificationService 생성 (웹훅 알림) - Phase 4: BackupCheckCommand/StatMonitorService에 Slack 알림 연동 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
136 lines
4.1 KiB
PHP
136 lines
4.1 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class SlackNotificationService
|
|
{
|
|
private ?string $webhookUrl;
|
|
|
|
private string $serverName;
|
|
|
|
private bool $enabled;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->webhookUrl = env('SLACK_ALERT_WEBHOOK_URL') ?: env('LOG_SLACK_WEBHOOK_URL');
|
|
$this->serverName = env('SLACK_ALERT_SERVER_NAME', config('app.env', 'unknown'));
|
|
$this->enabled = (bool) env('SLACK_ALERT_ENABLED', false);
|
|
}
|
|
|
|
/**
|
|
* 일반 알림 전송
|
|
*/
|
|
public function sendAlert(string $title, string $message, string $severity = 'critical'): void
|
|
{
|
|
$color = match ($severity) {
|
|
'critical' => '#FF0000',
|
|
'warning' => '#FFA500',
|
|
default => '#3498DB',
|
|
};
|
|
|
|
$emoji = match ($severity) {
|
|
'critical' => ':rotating_light:',
|
|
'warning' => ':warning:',
|
|
default => ':information_source:',
|
|
};
|
|
|
|
$this->send([
|
|
'attachments' => [
|
|
[
|
|
'color' => $color,
|
|
'blocks' => [
|
|
[
|
|
'type' => 'header',
|
|
'text' => [
|
|
'type' => 'plain_text',
|
|
'text' => "{$emoji} {$title}",
|
|
'emoji' => true,
|
|
],
|
|
],
|
|
[
|
|
'type' => 'section',
|
|
'fields' => [
|
|
[
|
|
'type' => 'mrkdwn',
|
|
'text' => "*서버:*\n{$this->serverName}",
|
|
],
|
|
[
|
|
'type' => 'mrkdwn',
|
|
'text' => '*시간:*\n' . now()->format('Y-m-d H:i:s'),
|
|
],
|
|
],
|
|
],
|
|
[
|
|
'type' => 'section',
|
|
'text' => [
|
|
'type' => 'mrkdwn',
|
|
'text' => "*상세:*\n{$message}",
|
|
],
|
|
],
|
|
[
|
|
'type' => 'context',
|
|
'elements' => [
|
|
[
|
|
'type' => 'mrkdwn',
|
|
'text' => "환경: `" . config('app.env') . "` | 심각도: `{$severity}`",
|
|
],
|
|
],
|
|
],
|
|
],
|
|
],
|
|
],
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 백업 실패 알림
|
|
*/
|
|
public function sendBackupAlert(string $title, string $message): void
|
|
{
|
|
$this->sendAlert("[SAM 백업 실패] {$title}", $message, 'critical');
|
|
}
|
|
|
|
/**
|
|
* 통계/모니터링 알림
|
|
*/
|
|
public function sendStatAlert(string $title, string $message, string $domain): void
|
|
{
|
|
$this->sendAlert("[SAM {$domain}] {$title}", $message, 'critical');
|
|
}
|
|
|
|
/**
|
|
* Slack 웹훅으로 메시지 전송
|
|
*/
|
|
private function send(array $payload): void
|
|
{
|
|
if (! $this->enabled) {
|
|
Log::debug('Slack 알림 비활성화 상태 (SLACK_ALERT_ENABLED=false)');
|
|
|
|
return;
|
|
}
|
|
|
|
if (empty($this->webhookUrl)) {
|
|
Log::warning('Slack 웹훅 URL 미설정');
|
|
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$response = Http::timeout(10)->post($this->webhookUrl, $payload);
|
|
|
|
if (! $response->successful()) {
|
|
Log::error('Slack 알림 전송 실패', [
|
|
'status' => $response->status(),
|
|
'body' => $response->body(),
|
|
]);
|
|
}
|
|
} catch (\Throwable $e) {
|
|
Log::error('Slack 알림 전송 예외', [
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
}
|
|
}
|
|
} |