From f548a9576775a6feb37ec3c5a8ea5422870da23a Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Tue, 17 Mar 2026 15:16:14 +0900 Subject: [PATCH] =?UTF-8?q?feat(tkuser):=20=EC=95=8C=EB=A6=BC=20=EC=88=98?= =?UTF-8?q?=EC=8B=A0=EC=9E=90=20=ED=83=AD=EC=97=90=20ntfy=20=EA=B5=AC?= =?UTF-8?q?=EB=8F=85=20=EA=B4=80=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - notificationRecipientModel에 ntfy CRUD 메서드 추가 (같은 DB 직접 쿼리) - ntfy 라우트 3개 추가 (GET/POST/DELETE, /:type 위에 배치) - 알림 수신자 탭 상단에 ntfy 구독 관리 카드 렌더링 - ntfy 추가 모달에 앱 설정 안내 문구 포함 Co-Authored-By: Claude Opus 4.6 --- .../notificationRecipientController.js | 46 +++++++++ .../api/models/notificationRecipientModel.js | 23 +++++ .../api/routes/notificationRecipientRoutes.js | 5 + user-management/web/index.html | 20 +++- .../js/tkuser-notificationRecipients.js | 96 ++++++++++++++++++- 5 files changed, 187 insertions(+), 3 deletions(-) diff --git a/user-management/api/controllers/notificationRecipientController.js b/user-management/api/controllers/notificationRecipientController.js index d656a7a..e8ec1c6 100644 --- a/user-management/api/controllers/notificationRecipientController.js +++ b/user-management/api/controllers/notificationRecipientController.js @@ -83,6 +83,52 @@ const notificationRecipientController = { } }, + // === ntfy 구독 관리 === + + // ntfy 구독자 목록 + getNtfySubscribers: async (req, res) => { + try { + const subscribers = await notificationRecipientModel.getNtfySubscribers(); + res.json({ success: true, data: subscribers }); + } catch (error) { + console.error('ntfy 구독자 조회 오류:', error); + res.status(500).json({ success: false, error: 'ntfy 구독자 조회 실패' }); + } + }, + + // ntfy 구독 등록 (관리자) + addNtfySubscription: async (req, res) => { + try { + const { userId } = req.params; + if (!userId) { + return res.status(400).json({ success: false, error: '사용자 ID가 필요합니다.' }); + } + if (!await checkNrPermission(req.user)) { + return res.status(403).json({ success: false, error: '알림 수신자 관리 권한이 없습니다.' }); + } + await notificationRecipientModel.addNtfySubscription(Number(userId)); + res.json({ success: true, message: 'ntfy 구독이 등록되었습니다.' }); + } catch (error) { + console.error('ntfy 구독 등록 오류:', error); + res.status(500).json({ success: false, error: 'ntfy 구독 등록 실패' }); + } + }, + + // ntfy 구독 해제 (관리자) + removeNtfySubscription: async (req, res) => { + try { + const { userId } = req.params; + if (!await checkNrPermission(req.user)) { + return res.status(403).json({ success: false, error: '알림 수신자 관리 권한이 없습니다.' }); + } + await notificationRecipientModel.removeNtfySubscription(Number(userId)); + res.json({ success: true, message: 'ntfy 구독이 해제되었습니다.' }); + } catch (error) { + console.error('ntfy 구독 해제 오류:', error); + res.status(500).json({ success: false, error: 'ntfy 구독 해제 실패' }); + } + }, + // 유형별 수신자 일괄 설정 setRecipients: async (req, res) => { try { diff --git a/user-management/api/models/notificationRecipientModel.js b/user-management/api/models/notificationRecipientModel.js index 2d31edf..5db93ea 100644 --- a/user-management/api/models/notificationRecipientModel.js +++ b/user-management/api/models/notificationRecipientModel.js @@ -138,6 +138,29 @@ const notificationRecipientModel = { return !!row; }, + // === ntfy 구독 관리 === + + async getNtfySubscribers() { + const db = getPool(); + const [rows] = await db.query(` + SELECT ns.user_id, ns.created_at, u.username, u.name as user_name + FROM ntfy_subscriptions ns + JOIN sso_users u ON u.user_id = ns.user_id + ORDER BY u.name + `); + return rows; + }, + + async addNtfySubscription(userId) { + const db = getPool(); + await db.query('INSERT IGNORE INTO ntfy_subscriptions (user_id) VALUES (?)', [userId]); + }, + + async removeNtfySubscription(userId) { + const db = getPool(); + await db.query('DELETE FROM ntfy_subscriptions WHERE user_id = ?', [userId]); + }, + // 유형별 수신자 일괄 설정 async setRecipients(notificationType, userIds, createdBy = null) { const db = getPool(); diff --git a/user-management/api/routes/notificationRecipientRoutes.js b/user-management/api/routes/notificationRecipientRoutes.js index e536f94..6cac7c4 100644 --- a/user-management/api/routes/notificationRecipientRoutes.js +++ b/user-management/api/routes/notificationRecipientRoutes.js @@ -13,6 +13,11 @@ router.get('/types', controller.getTypes); // 전체 수신자 목록 (유형별 그룹화) router.get('/', controller.getAll); +// ntfy 구독 관리 (/:type보다 위에 배치해야 "ntfy"를 type으로 잡지 않음) +router.get('/ntfy', controller.getNtfySubscribers); +router.post('/ntfy/:userId', controller.addNtfySubscription); +router.delete('/ntfy/:userId', controller.removeNtfySubscription); + // 유형별 수신자 조회 router.get('/:type', controller.getByType); diff --git a/user-management/web/index.html b/user-management/web/index.html index d884492..a1538cc 100644 --- a/user-management/web/index.html +++ b/user-management/web/index.html @@ -1582,6 +1582,24 @@ + + +