fix(notification): 비활성 확장이 제공하던 알림 채널을 발송 대상에서 제외

비활성 상태의 확장이 등록했던 발송 수단(문자·알림톡 등)을 코어 via가
저장된 채널 설정만 보고 계속 발송·"건너뜀" 로깅하던 문제를 수정한다.
사용 가능한 채널 목록에 없는 채널은 로그 없이 조용히 제외하며,
알림 설정·템플릿은 그대로 유지되어 확장을 다시 켜면 복구된다.

아울러 발송이 타임아웃·연결실패로 최종 실패할 때 pending 으로 방치되던
이력을 failed 로 마감한다(webhook 이 먼저 확정한 건은 덮어쓰지 않음).
This commit is contained in:
chym1217 committed 2026-07-28 12:40:13 +09:00
1 parent cd11fc60ce
commit 8aa395c3a1
6 files changed
+186 -5

No files matched your search

+39
View File
@@ -67,6 +67,14 @@ class GenericNotification extends BaseNotification
// 대상 채널이 지정된 경우 (template별 발송) — 게스트 게이트 + 확장 토글 + readiness 확인
if ($this->channel !== null) {
try {
// 사용 가능한 채널 목록에 없는 채널은 발송 후보에서 제외한다 (로그도 남기지 않음).
// 채널을 제공하던 확장이 비활성/삭제되면 그 채널이 getAvailableChannels() 에서 빠지지만,
// 저장된 채널 설정·템플릿은 남아 있어 죽은 채널로 발송·"건너뜀" 로깅이 발생한다. 살아 있지
// 않은 채널은 "존재하지 않는 것"으로 취급해 조용히 제외한다(skipped 기록 대상 아님).
if (! $this->isChannelAvailable($this->channel)) {
return [];
}
// 게스트 게이트: 비회원 수신자인데 채널이 게스트 발송 미허용이면 제외
if ($this->isChannelBlockedForGuest($notifiable, $this->channel)) {
$this->logSkippedChannels([
@@ -126,6 +134,12 @@ class GenericNotification extends BaseNotification
$skippedChannels = [];
foreach ($channels as $channel) {
// 사용 가능한 채널 목록에 없는 채널(비활성/삭제된 확장이 제공하던 채널)은 로그 없이
// 조용히 제외한다 — 죽은 채널을 "건너뜀"으로 기록하지 않는다(채널 지정 경로와 동일 정책).
if (! $this->isChannelAvailable($channel)) {
continue;
}
// 게스트 게이트: 비회원 수신자인데 채널이 게스트 발송 미허용이면 제외
if ($this->isChannelBlockedForGuest($notifiable, $channel)) {
$skippedChannels[] = [
@@ -194,6 +208,31 @@ class GenericNotification extends BaseNotification
}
}
/**
* 채널이 현재 사용 가능한(살아 있는) 채널 목록에 있는지 확인합니다.
*
* getAvailableChannels() 는 코어 기본 채널(mail/database)과 활성 확장이 filter 훅으로 추가한
* 채널만 담는다. 확장이 비활성/삭제되면 그 채널이 이 목록에서 자동으로 빠지므로, 특정 확장
* 이름을 알지 못해도 "죽은 채널"을 판별할 수 있다. 조회 실패 시(부팅 초기 등) 안전측으로 true
* 를 반환해 기존 게이트(확장 토글·readiness)가 판정하도록 위임한다.
*
* @param string $channel 채널 식별자
* @return bool 사용 가능하면 true
*/
private function isChannelAvailable(string $channel): bool
{
try {
$availableIds = array_column(
app(NotificationChannelService::class)->getAvailableChannels(),
'id'
);
return in_array($channel, $availableIds, true);
} catch (\Throwable $e) {
return true;
}
}
/**
* 비회원 수신자에게 해당 채널 발송이 차단되는지 확인합니다.
*