feat(ntfy): Phase 2 — sendPushToUsers() ntfy 연동 + 구독 관리 UI
- ntfy_subscriptions 테이블 마이그레이션 추가 - ntfySender.js 유틸 (ntfy HTTP POST 래퍼) - sendPushToUsers() ntfy 우선 분기 (ntfy 구독자 → ntfy, 나머지 → Web Push) - ntfy subscribe/unsubscribe/status API 엔드포인트 - notification-bell.js ntfy 토글 버튼 + 앱 설정 안내 모달 - docker-compose system1-api에 NTFY 환경변수 추가 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -24,19 +24,44 @@ function getWebPush() {
|
||||
return vapidConfigured ? webpush : null;
|
||||
}
|
||||
|
||||
// Push 전송 헬퍼 — 알림 생성 후 호출
|
||||
// Push 전송 헬퍼 — 알림 생성 후 호출 (ntfy 우선, 나머지 Web Push)
|
||||
async function sendPushToUsers(userIds, payload) {
|
||||
const wp = getWebPush();
|
||||
if (!wp) return;
|
||||
const pushModel = require('./pushSubscriptionModel');
|
||||
const { sendNtfy } = require('../utils/ntfySender');
|
||||
|
||||
try {
|
||||
const pushModel = require('./pushSubscriptionModel');
|
||||
const subscriptions = userIds && userIds.length > 0
|
||||
? await pushModel.getByUserIds(userIds)
|
||||
: await pushModel.getAll(); // broadcast
|
||||
// 1) ntfy 구독자 분리
|
||||
const ntfyUserIds = userIds && userIds.length > 0
|
||||
? await pushModel.getNtfyUserIds(userIds)
|
||||
: await pushModel.getAllNtfyUserIds();
|
||||
|
||||
const ntfySet = new Set(ntfyUserIds);
|
||||
|
||||
// 2) ntfy 병렬 발송
|
||||
if (ntfyUserIds.length > 0) {
|
||||
await Promise.allSettled(
|
||||
ntfyUserIds.map(uid =>
|
||||
sendNtfy(uid, { title: payload.title, body: payload.body, url: payload.url })
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// 3) Web Push — ntfy 구독자 제외
|
||||
const wp = getWebPush();
|
||||
if (!wp) return;
|
||||
|
||||
let subscriptions;
|
||||
if (userIds && userIds.length > 0) {
|
||||
const webPushUserIds = userIds.filter(id => !ntfySet.has(id));
|
||||
subscriptions = webPushUserIds.length > 0
|
||||
? await pushModel.getByUserIds(webPushUserIds)
|
||||
: [];
|
||||
} else {
|
||||
// broadcast: 전체 구독 가져온 뒤 ntfy 사용자 제외
|
||||
subscriptions = (await pushModel.getAll()).filter(s => !ntfySet.has(s.user_id));
|
||||
}
|
||||
|
||||
const payloadStr = JSON.stringify(payload);
|
||||
|
||||
for (const sub of subscriptions) {
|
||||
try {
|
||||
await wp.sendNotification({
|
||||
@@ -44,7 +69,6 @@ async function sendPushToUsers(userIds, payload) {
|
||||
keys: { p256dh: sub.p256dh, auth: sub.auth }
|
||||
}, payloadStr);
|
||||
} catch (err) {
|
||||
// 만료 구독 (410 Gone, 404 Not Found) 자동 정리
|
||||
if (err.statusCode === 410 || err.statusCode === 404) {
|
||||
await pushModel.deleteByEndpoint(sub.endpoint).catch(() => {});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user