mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-20 22:01:45 +00:00
feat(notifications): add ntfy as a first-class notification channel
Adds ntfy.sh (and self-hosted instances) as a new push notification channel with full parity to the existing webhook channel. - Backend: NtfyConfig type, getUserNtfyConfig, getAdminNtfyConfig, resolveNtfyUrl, sendNtfy (header-based API with Title/Priority/Tags/ Click headers), testNtfy, NTFY_EVENT_META (priority + emoji tags per event), SSRF guard via existing checkSsrf + createPinnedDispatcher - notificationPreferencesService: ntfy added to NotifChannel union, IMPLEMENTED_COMBOS, getActiveChannels parser, getAvailableChannels, ADMIN_GLOBAL_CHANNELS, and AvailableChannels interface - notificationService: per-user ntfy dispatch after webhook block; admin-scoped ntfy via getAdminGlobalPref for version_available events - Routes: POST /api/notifications/test-ntfy with saved-token fallback - authService: admin_ntfy_server/topic/token in ADMIN_SETTINGS_KEYS, masked + encrypted on read/write - settingsService: ntfy_token added to ENCRYPTED_SETTING_KEYS - Frontend: ntfy topic/server/token inputs + Save/Test/Clear buttons in NotificationsTab; admin Ntfy panel in AdminPage; testNtfy API method - i18n: full English strings; English placeholders in 14 other locales - Tests: resolveNtfyUrl, sendNtfy, dispatch integration, UI tests, MSW handler for test-ntfy endpoint
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import express, { Request, Response } from 'express';
|
||||
import { authenticate } from '../middleware/auth';
|
||||
import { AuthRequest } from '../types';
|
||||
import { testSmtp, testWebhook, getAdminWebhookUrl, getUserWebhookUrl } from '../services/notifications';
|
||||
import { testSmtp, testWebhook, testNtfy, getAdminWebhookUrl, getUserWebhookUrl, getUserNtfyConfig, getAdminNtfyConfig } from '../services/notifications';
|
||||
import {
|
||||
getNotifications,
|
||||
getUnreadCount,
|
||||
@@ -47,6 +47,26 @@ router.post('/test-webhook', authenticate, async (req: Request, res: Response) =
|
||||
res.json(await testWebhook(url));
|
||||
});
|
||||
|
||||
router.post('/test-ntfy', authenticate, async (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { topic, server, token } = req.body as { topic?: string; server?: string; token?: string };
|
||||
|
||||
// Always load saved config for fallbacks (token may be masked or absent in request)
|
||||
const userCfg = getUserNtfyConfig(authReq.user.id);
|
||||
const adminCfg = getAdminNtfyConfig();
|
||||
|
||||
const resolvedTopic = topic || userCfg?.topic || undefined;
|
||||
const resolvedServer = server || userCfg?.server || adminCfg.server || undefined;
|
||||
// Reuse saved token when request sends null, empty, or the masked placeholder
|
||||
const resolvedToken = (token && token !== '••••••••')
|
||||
? token
|
||||
: (userCfg?.token ?? adminCfg.token ?? null);
|
||||
|
||||
if (!resolvedTopic) return res.status(400).json({ error: 'No ntfy topic configured' });
|
||||
|
||||
res.json(await testNtfy({ topic: resolvedTopic, server: resolvedServer ?? null, token: resolvedToken }));
|
||||
});
|
||||
|
||||
// ── In-app notifications ──────────────────────────────────────────────────────
|
||||
|
||||
// GET /in-app — list notifications (paginated)
|
||||
|
||||
Reference in New Issue
Block a user