From 0257e0d842c49b79e877e4e8d92194bb4cadcac1 Mon Sep 17 00:00:00 2001 From: jubnl Date: Sat, 11 Apr 2026 18:36:35 +0200 Subject: [PATCH] feat: route Synology session-cleared notification through unified send() Replace direct createNotification() call with notificationService.send() so the notification respects user preferences and reaches all enabled channels (in-app, email, webhook) instead of only WebSocket. Registers synology_session_cleared as a proper NotifEventType (inapp-only) and adds localized text for all 14 supported languages. --- server/src/services/memories/synologyService.ts | 13 ++++++------- .../services/notificationPreferencesService.ts | 4 +++- server/src/services/notificationService.ts | 6 ++++++ server/src/services/notifications.ts | 15 +++++++++++++++ 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/server/src/services/memories/synologyService.ts b/server/src/services/memories/synologyService.ts index 17689be9..d15ea3f0 100644 --- a/server/src/services/memories/synologyService.ts +++ b/server/src/services/memories/synologyService.ts @@ -19,7 +19,7 @@ import { SyncAlbumResult, AssetInfo } from './helpersService'; -import { createNotification } from '../inAppNotifications'; +import { send as sendNotification } from '../notificationService'; const SYNOLOGY_PROVIDER = 'synologyphotos'; // Users provide the full base URL including the Photos app path (e.g. https://nas:5001/photo). @@ -374,13 +374,12 @@ export async function updateSynologySettings(userId: number, synologyUrl: string const sessionCleared = urlChanged || userChanged; if (sessionCleared) { _clearSynologySession(userId); - createNotification({ - type: 'simple', + sendNotification({ + event: 'synology_session_cleared', + actorId: null, + params: {}, scope: 'user', - target: userId, - sender_id: null, - title_key: 'notifications.synologySessionCleared.title', - text_key: 'notifications.synologySessionCleared.text', + targetId: userId, }); } diff --git a/server/src/services/notificationPreferencesService.ts b/server/src/services/notificationPreferencesService.ts index f8380fd5..5b7ef73f 100644 --- a/server/src/services/notificationPreferencesService.ts +++ b/server/src/services/notificationPreferencesService.ts @@ -13,7 +13,8 @@ export type NotifEventType = | 'photos_shared' | 'collab_message' | 'packing_tagged' - | 'version_available'; + | 'version_available' + | 'synology_session_cleared'; export interface AvailableChannels { email: boolean; @@ -32,6 +33,7 @@ const IMPLEMENTED_COMBOS: Record = { collab_message: ['inapp', 'email', 'webhook'], packing_tagged: ['inapp', 'email', 'webhook'], version_available: ['inapp', 'email', 'webhook'], + synology_session_cleared: ['inapp'], }; /** Events that target admins only (shown in admin panel, not in user settings). */ diff --git a/server/src/services/notificationService.ts b/server/src/services/notificationService.ts index 6fdc1eed..243faaf7 100644 --- a/server/src/services/notificationService.ts +++ b/server/src/services/notificationService.ts @@ -112,6 +112,12 @@ const EVENT_NOTIFICATION_CONFIG: Record = { navigateTextKey: 'notif.action.view_admin', navigateTarget: () => '/admin', }, + synology_session_cleared: { + inAppType: 'simple', + titleKey: 'notifications.synologySessionCleared.title', + textKey: 'notifications.synologySessionCleared.text', + navigateTarget: () => null, + }, }; // ── Fallback config for unknown event types ──────────────────────────────── diff --git a/server/src/services/notifications.ts b/server/src/services/notifications.ts index b4d3a26e..56363404 100644 --- a/server/src/services/notifications.ts +++ b/server/src/services/notifications.ts @@ -104,6 +104,7 @@ const EVENT_TEXTS: Record> = { collab_message: p => ({ title: `New message in "${p.trip}"`, body: `${p.actor}: ${p.preview}` }), packing_tagged: p => ({ title: `Packing: ${p.category}`, body: `${p.actor} assigned you to the "${p.category}" packing category in "${p.trip}".` }), version_available: p => ({ title: 'New TREK version available', body: `TREK ${p.version} is now available. Visit the admin panel to update.` }), + synology_session_cleared: () => ({ title: 'Synology session cleared', body: 'Your Synology account or URL changed. You have been logged out of Synology Photos.' }), }, de: { trip_invite: p => ({ title: `Einladung zu "${p.trip}"`, body: `${p.actor} hat ${p.invitee || 'ein Mitglied'} zur Reise "${p.trip}" eingeladen.` }), @@ -114,6 +115,7 @@ const EVENT_TEXTS: Record> = { collab_message: p => ({ title: `Neue Nachricht in "${p.trip}"`, body: `${p.actor}: ${p.preview}` }), packing_tagged: p => ({ title: `Packliste: ${p.category}`, body: `${p.actor} hat dich der Kategorie "${p.category}" in der Packliste von "${p.trip}" zugewiesen.` }), version_available: p => ({ title: 'Neue TREK-Version verfügbar', body: `TREK ${p.version} ist jetzt verfügbar. Besuche das Admin-Panel zum Aktualisieren.` }), + synology_session_cleared: () => ({ title: 'Synology-Sitzung beendet', body: 'Dein Synology-Konto oder die URL hat sich geändert. Du wurdest von Synology Photos abgemeldet.' }), }, fr: { trip_invite: p => ({ title: `Invitation à "${p.trip}"`, body: `${p.actor} a invité ${p.invitee || 'un membre'} au voyage "${p.trip}".` }), @@ -124,6 +126,7 @@ const EVENT_TEXTS: Record> = { collab_message: p => ({ title: `Nouveau message dans "${p.trip}"`, body: `${p.actor} : ${p.preview}` }), packing_tagged: p => ({ title: `Bagages : ${p.category}`, body: `${p.actor} vous a assigné à la catégorie "${p.category}" dans "${p.trip}".` }), version_available: p => ({ title: 'Nouvelle version TREK disponible', body: `TREK ${p.version} est maintenant disponible. Rendez-vous dans le panneau d'administration pour mettre à jour.` }), + synology_session_cleared: () => ({ title: 'Session Synology effacée', body: 'Votre compte ou URL Synology a changé. Vous avez été déconnecté de Synology Photos.' }), }, es: { trip_invite: p => ({ title: `Invitación a "${p.trip}"`, body: `${p.actor} invitó a ${p.invitee || 'un miembro'} al viaje "${p.trip}".` }), @@ -134,6 +137,7 @@ const EVENT_TEXTS: Record> = { collab_message: p => ({ title: `Nuevo mensaje en "${p.trip}"`, body: `${p.actor}: ${p.preview}` }), packing_tagged: p => ({ title: `Equipaje: ${p.category}`, body: `${p.actor} te asignó a la categoría "${p.category}" en "${p.trip}".` }), version_available: p => ({ title: 'Nueva versión de TREK disponible', body: `TREK ${p.version} ya está disponible. Visita el panel de administración para actualizar.` }), + synology_session_cleared: () => ({ title: 'Sesión de Synology cerrada', body: 'Tu cuenta o URL de Synology ha cambiado. Has cerrado sesión en Synology Photos.' }), }, nl: { trip_invite: p => ({ title: `Uitnodiging voor "${p.trip}"`, body: `${p.actor} heeft ${p.invitee || 'een lid'} uitgenodigd voor de reis "${p.trip}".` }), @@ -144,6 +148,7 @@ const EVENT_TEXTS: Record> = { collab_message: p => ({ title: `Nieuw bericht in "${p.trip}"`, body: `${p.actor}: ${p.preview}` }), packing_tagged: p => ({ title: `Paklijst: ${p.category}`, body: `${p.actor} heeft je toegewezen aan de categorie "${p.category}" in "${p.trip}".` }), version_available: p => ({ title: 'Nieuwe TREK-versie beschikbaar', body: `TREK ${p.version} is nu beschikbaar. Bezoek het beheerderspaneel om bij te werken.` }), + synology_session_cleared: () => ({ title: 'Synology-sessie gewist', body: 'Je Synology-account of URL is gewijzigd. Je bent uitgelogd bij Synology Photos.' }), }, ru: { trip_invite: p => ({ title: `Приглашение в "${p.trip}"`, body: `${p.actor} пригласил ${p.invitee || 'участника'} в поездку "${p.trip}".` }), @@ -154,6 +159,7 @@ const EVENT_TEXTS: Record> = { collab_message: p => ({ title: `Новое сообщение в "${p.trip}"`, body: `${p.actor}: ${p.preview}` }), packing_tagged: p => ({ title: `Список вещей: ${p.category}`, body: `${p.actor} назначил вас в категорию "${p.category}" в "${p.trip}".` }), version_available: p => ({ title: 'Доступна новая версия TREK', body: `TREK ${p.version} теперь доступен. Перейдите в панель администратора для обновления.` }), + synology_session_cleared: () => ({ title: 'Сессия Synology сброшена', body: 'Ваш аккаунт или URL Synology изменился. Вы вышли из Synology Photos.' }), }, zh: { trip_invite: p => ({ title: `邀请加入"${p.trip}"`, body: `${p.actor} 邀请了 ${p.invitee || '成员'} 加入旅行"${p.trip}"。` }), @@ -164,6 +170,7 @@ const EVENT_TEXTS: Record> = { collab_message: p => ({ title: `"${p.trip}"中的新消息`, body: `${p.actor}:${p.preview}` }), packing_tagged: p => ({ title: `行李清单:${p.category}`, body: `${p.actor} 将你分配到"${p.trip}"中的"${p.category}"类别。` }), version_available: p => ({ title: '新版 TREK 可用', body: `TREK ${p.version} 现已可用。请前往管理面板进行更新。` }), + synology_session_cleared: () => ({ title: 'Synology 会话已清除', body: '您的 Synology 账户或 URL 已更改,您已退出 Synology Photos。' }), }, 'zh-TW': { trip_invite: p => ({ title: `邀請加入「${p.trip}」`, body: `${p.actor} 邀請了 ${p.invitee || '成員'} 加入行程「${p.trip}」。` }), @@ -173,6 +180,8 @@ const EVENT_TEXTS: Record> = { photos_shared: p => ({ title: `已分享 ${p.count} 張照片`, body: `${p.actor} 在「${p.trip}」中分享了 ${p.count} 張照片。` }), collab_message: p => ({ title: `「${p.trip}」中的新訊息`, body: `${p.actor}:${p.preview}` }), packing_tagged: p => ({ title: `打包清單:${p.category}`, body: `${p.actor} 已將您指派到「${p.trip}」中的「${p.category}」分類。` }), + version_available: p => ({ title: '新版 TREK 可用', body: `TREK ${p.version} 現已可用。請前往管理面板進行更新。` }), + synology_session_cleared: () => ({ title: 'Synology 工作階段已清除', body: '您的 Synology 帳戶或 URL 已變更,您已登出 Synology Photos。' }), }, ar: { trip_invite: p => ({ title: `دعوة إلى "${p.trip}"`, body: `${p.actor} دعا ${p.invitee || 'عضو'} إلى الرحلة "${p.trip}".` }), @@ -183,6 +192,7 @@ const EVENT_TEXTS: Record> = { collab_message: p => ({ title: `رسالة جديدة في "${p.trip}"`, body: `${p.actor}: ${p.preview}` }), packing_tagged: p => ({ title: `قائمة التعبئة: ${p.category}`, body: `${p.actor} عيّنك في فئة "${p.category}" في "${p.trip}".` }), version_available: p => ({ title: 'إصدار TREK جديد متاح', body: `TREK ${p.version} متاح الآن. تفضل بزيارة لوحة الإدارة للتحديث.` }), + synology_session_cleared: () => ({ title: 'تمت إعادة تعيين جلسة Synology', body: 'تغيّر حسابك أو رابط Synology. تم تسجيل خروجك من Synology Photos.' }), }, br: { trip_invite: p => ({ title: `Convite para "${p.trip}"`, body: `${p.actor} convidou ${p.invitee || 'um membro'} para a viagem "${p.trip}".` }), @@ -193,6 +203,7 @@ const EVENT_TEXTS: Record> = { collab_message: p => ({ title: `Nova mensagem em "${p.trip}"`, body: `${p.actor}: ${p.preview}` }), packing_tagged: p => ({ title: `Bagagem: ${p.category}`, body: `${p.actor} atribuiu você à categoria "${p.category}" em "${p.trip}".` }), version_available: p => ({ title: 'Nova versão do TREK disponível', body: `O TREK ${p.version} está disponível. Acesse o painel de administração para atualizar.` }), + synology_session_cleared: () => ({ title: 'Sessão Synology encerrada', body: 'Sua conta ou URL do Synology foi alterada. Você foi desconectado do Synology Photos.' }), }, cs: { trip_invite: p => ({ title: `Pozvánka do "${p.trip}"`, body: `${p.actor} pozval ${p.invitee || 'člena'} na výlet "${p.trip}".` }), @@ -203,6 +214,7 @@ const EVENT_TEXTS: Record> = { collab_message: p => ({ title: `Nová zpráva v "${p.trip}"`, body: `${p.actor}: ${p.preview}` }), packing_tagged: p => ({ title: `Balení: ${p.category}`, body: `${p.actor} vás přiřadil do kategorie "${p.category}" v "${p.trip}".` }), version_available: p => ({ title: 'Nová verze TREK dostupná', body: `TREK ${p.version} je nyní dostupný. Navštivte administrátorský panel pro aktualizaci.` }), + synology_session_cleared: () => ({ title: 'Relace Synology byla zrušena', body: 'Váš účet nebo URL Synology se změnil. Byli jste odhlášeni ze Synology Photos.' }), }, hu: { trip_invite: p => ({ title: `Meghívó a(z) "${p.trip}" utazásra`, body: `${p.actor} meghívta ${p.invitee || 'egy tagot'} a(z) "${p.trip}" utazásra.` }), @@ -213,6 +225,7 @@ const EVENT_TEXTS: Record> = { collab_message: p => ({ title: `Új üzenet a(z) "${p.trip}" utazásban`, body: `${p.actor}: ${p.preview}` }), packing_tagged: p => ({ title: `Csomagolás: ${p.category}`, body: `${p.actor} hozzárendelte Önt a "${p.category}" csomagolási kategóriához a(z) "${p.trip}" utazásban.` }), version_available: p => ({ title: 'Új TREK verzió érhető el', body: `A TREK ${p.version} elérhető. Látogasson el az adminisztrációs panelre a frissítéshez.` }), + synology_session_cleared: () => ({ title: 'Synology munkamenet törölve', body: 'A Synology fiókja vagy URL-je megváltozott. Kijelentkeztek a Synology Photos-ból.' }), }, it: { trip_invite: p => ({ title: `Invito a "${p.trip}"`, body: `${p.actor} ha invitato ${p.invitee || 'un membro'} al viaggio "${p.trip}".` }), @@ -223,6 +236,7 @@ const EVENT_TEXTS: Record> = { collab_message: p => ({ title: `Nuovo messaggio in "${p.trip}"`, body: `${p.actor}: ${p.preview}` }), packing_tagged: p => ({ title: `Bagagli: ${p.category}`, body: `${p.actor} ti ha assegnato alla categoria "${p.category}" in "${p.trip}".` }), version_available: p => ({ title: 'Nuova versione TREK disponibile', body: `TREK ${p.version} è ora disponibile. Visita il pannello di amministrazione per aggiornare.` }), + synology_session_cleared: () => ({ title: 'Sessione Synology rimossa', body: 'Il tuo account o URL Synology è cambiato. Sei stato disconnesso da Synology Photos.' }), }, pl: { trip_invite: p => ({ title: `Zaproszenie do "${p.trip}"`, body: `${p.actor} zaprosił ${p.invitee || 'członka'} do podróży "${p.trip}".` }), @@ -233,6 +247,7 @@ const EVENT_TEXTS: Record> = { collab_message: p => ({ title: `Nowa wiadomość w "${p.trip}"`, body: `${p.actor}: ${p.preview}` }), packing_tagged: p => ({ title: `Pakowanie: ${p.category}`, body: `${p.actor} przypisał Cię do kategorii "${p.category}" w "${p.trip}".` }), version_available: p => ({ title: 'Nowa wersja TREK dostępna', body: `TREK ${p.version} jest teraz dostępny. Odwiedź panel administracyjny, aby zaktualizować.` }), + synology_session_cleared: () => ({ title: 'Sesja Synology wyczyszczona', body: 'Twoje konto lub URL Synology uległo zmianie. Zostałeś wylogowany z Synology Photos.' }), }, };