feat: DB 백업 시스템 구축 (Phase 1,2,4)
- 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>
This commit is contained in:
136
app/Services/SlackNotificationService.php
Normal file
136
app/Services/SlackNotificationService.php
Normal file
@@ -0,0 +1,136 @@
|
||||
<?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(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user