feat(notifications): surface guest activity in the admin bell (#849)
* feat(notifications): surface guest activity in the admin bell (#746) Favorites already reached activity_logs (feedbackService), but gallery opens and downloads only landed in access_logs — invisible in the notification bell. Now: - gallery_opened on the guest photo-list route, debounced in-memory to one notification per event per 6h (the endpoint fires per page load; per-hit notifications would spam the bell). Slideshow traffic stays excluded, matching the analytics exclusion. - gallery_downloaded on all four download paths (streamed + pre-zipped + presigned download-all, download-selected) with scope metadata. - Frontend: locale entries for galleryOpened/galleryDownloaded (and photoFavorite, which previously fell through to the generic 'system activity' line) in all 8 languages — resolved via the existing smart camelCase fallback, no switch cases needed. Distinct bell icons per type. * fix(notifications): single-photo download activity + render per-type bell icons (codex review of #849) - The per-photo Save route (GET /:slug/download/:photoId) only wrote to access_logs — the most common download path never reached the bell. Now emits gallery_downloaded with scope 'single', debounced to one notification per event per hour: a guest saving 30 photos is one signal, not thirty (exact counts stay in access_logs/analytics). - getNotificationStyle's icon names were dead — AdminHeader hard-coded <Bell> for every row. Added an icon map so gallery opens (Eye), downloads (Download), favorites (Heart) and the pre-existing style names render their intended icons. * fix(notifications): notify after successful delivery, complete the icon map (codex review of #849, round 2) - Single-photo notification now fires on res 'finish' with status < 400: emitting up-front logged downloads that then 404ed/failed AND burned the 1h debounce window against the next real download. - Icon map completed over every name getNotificationStyle returns (grep-verified) — settings/user/mail/etc. styles render their declared icons instead of falling back to Bell. Deliberately NOT taken from the review: DB-backed debounce state for multi-worker deployments. The backend's current deployment contract is single-process (no PM2 cluster in-repo; multi-replica explicitly parked in #799 — chunked-upload/session state is process-local for the same reason). Worst case under a future multi-worker setup is N notifications per window, which degrades, not breaks; a shared-store debounce belongs to the #799 phase-3 work. * fix(notifications): attribute client sessions, log cached-ZIP after finish, add Trash2 icon (codex review of #849, round 3) - gallery_opened/gallery_downloaded now carry the real actor: client sessions (accessLevel 'client') are recorded as 'customer' instead of being mislabeled 'guest' — #746 explicitly covers client activity, so they are attributed, not excluded. - Cached-ZIP streaming path logs on res 'finish' (< 400) like the single-photo path — piping is not delivery. The presigned-redirect and on-the-fly-archiver paths keep their existing timing (redirect handoff / post-finalize). - Trash2 added to the icon map (customer_erased, bulk_delete_completed no longer fall back to Bell — the grep that built the map missed the digit in the name). * fix(notifications): dashboard formatting, portal dedup, actor-aware wording, archiver finish-hooks (codex review of #849, confirmation round) - activity_logs feed TWO surfaces: the dashboard's Recent Activity used admin.activities.<type> keys that didn't exist, rendering raw identifiers — added gallery_opened/gallery_downloaded entries in all 8 locales. - Customer-portal opens already log customer_event_access at the access-token mint; the ensuing /photos call no longer double-notifies (client sessions surface via downloads only). - gallery_downloaded formatting is actor-aware: customer sessions render 'Customer downloaded…' (new galleryDownloadedCustomer key ×8) instead of 'A guest…'. - Both on-the-fly ZIP paths (download-all fallback + download-selected) notify on res 'finish' < 400 — archive.finalize() ends Archiver's input, not the HTTP transfer. * fix(notifications): key customer dedup/attribution on portal provenance, neutral favorite wording (codex review of #849, final round) The previous dedup was inverted: portal-minted tokens carry via:'customer' but NO accessLevel (they run as guest), while PIN-client logins carry accessLevel:'client' and log nothing else. So PIN clients' only open signal was suppressed while portal opens still double- notified and portal downloads read as guest activity. verifyGalleryAccess now surfaces req.viaCustomer; gallery_opened dedups on THAT (portal only), and galleryActor treats via-customer OR accessLevel-client as 'customer'. photoFavorite wording is actor-neutral across all 8 locales — feedbackService logs favorites without an actor, so claiming 'a guest' was wrong for customer favorites.
This commit is contained in:
@@ -2497,6 +2497,10 @@
|
||||
"archiveDownloaded": "Archiv für \"{{eventName}}\" heruntergeladen",
|
||||
"archiveDeleted": "Archiv für \"{{eventName}}\" gelöscht",
|
||||
"archiveRestored": "Archiv für \"{{eventName}}\" wiederhergestellt",
|
||||
"galleryOpened": "Ein Gast hat die Galerie „{{eventName}}“ geöffnet",
|
||||
"galleryDownloaded": "Ein Gast hat Fotos aus „{{eventName}}“ heruntergeladen",
|
||||
"galleryDownloadedCustomer": "Kunde hat Fotos aus „{{eventName}}“ heruntergeladen",
|
||||
"photoFavorite": "Ein Foto in „{{eventName}}“ wurde favorisiert",
|
||||
"systemActivity": "Systemaktivität: {{type}}",
|
||||
"adminProfileUpdated": "Admin-Profil aktualisiert von {{actorName}}",
|
||||
"photosUploaded_one": "{{count}} Foto in \"{{eventName}}\" hochgeladen",
|
||||
@@ -2728,6 +2732,8 @@
|
||||
"analytics": "Analytik",
|
||||
"activities": {
|
||||
"event_created": "Neues Ereignis erstellt: {{eventName}}",
|
||||
"gallery_opened": "Galerie „{{eventName}}“ wurde geöffnet",
|
||||
"gallery_downloaded": "Fotos aus „{{eventName}}“ wurden heruntergeladen",
|
||||
"photos_uploaded": "{{count}} Fotos hochgeladen in {{eventName}}",
|
||||
"event_archived": "Ereignis archiviert: {{eventName}}",
|
||||
"archive_restored": "Archiv wiederhergestellt: {{eventName}}",
|
||||
|
||||
@@ -2073,6 +2073,10 @@
|
||||
"archiveDownloaded": "Archive downloaded for \"{{eventName}}\"",
|
||||
"archiveDeleted": "Archive deleted for \"{{eventName}}\"",
|
||||
"archiveRestored": "Archive restored for \"{{eventName}}\"",
|
||||
"galleryOpened": "A guest opened the gallery “{{eventName}}”",
|
||||
"galleryDownloaded": "A guest downloaded photos from “{{eventName}}”",
|
||||
"galleryDownloadedCustomer": "Customer downloaded photos from “{{eventName}}”",
|
||||
"photoFavorite": "A photo in “{{eventName}}” was favorited",
|
||||
"systemActivity": "System activity: {{type}}",
|
||||
"adminProfileUpdated": "Admin profile updated by {{actorName}}",
|
||||
"photosUploaded_one": "{{count}} photo uploaded to \"{{eventName}}\"",
|
||||
@@ -2306,6 +2310,8 @@
|
||||
"analytics": "Analytics",
|
||||
"activities": {
|
||||
"event_created": "New event created: {{eventName}}",
|
||||
"gallery_opened": "Gallery “{{eventName}}” was opened",
|
||||
"gallery_downloaded": "Photos were downloaded from “{{eventName}}”",
|
||||
"photos_uploaded": "{{count}} photos uploaded to {{eventName}}",
|
||||
"event_archived": "Event archived: {{eventName}}",
|
||||
"archive_restored": "Archive restored: {{eventName}}",
|
||||
|
||||
@@ -1423,6 +1423,10 @@
|
||||
"archiveDownloaded": "Archivo descargado para \"{{eventName}}\"",
|
||||
"archiveDeleted": "Archivo eliminado para \"{{eventName}}\"",
|
||||
"archiveRestored": "Archivo restaurado para \"{{eventName}}\"",
|
||||
"galleryOpened": "Un invitado abrió la galería «{{eventName}}»",
|
||||
"galleryDownloaded": "Un invitado descargó fotos de «{{eventName}}»",
|
||||
"galleryDownloadedCustomer": "El cliente descargó fotos de «{{eventName}}»",
|
||||
"photoFavorite": "Se marcó como favorita una foto de «{{eventName}}»",
|
||||
"systemActivity": "Actividad del sistema: {{type}}",
|
||||
"adminProfileUpdated": "Perfil de admin actualizado por {{actorName}}"
|
||||
},
|
||||
@@ -1467,6 +1471,8 @@
|
||||
"analytics": "Analíticas",
|
||||
"activities": {
|
||||
"event_created": "Nuevo evento creado: {{eventName}}",
|
||||
"gallery_opened": "Se abrió la galería «{{eventName}}»",
|
||||
"gallery_downloaded": "Se descargaron fotos de «{{eventName}}»",
|
||||
"photos_uploaded": "{{count}} fotos subidas a {{eventName}}",
|
||||
"event_archived": "Evento archivado: {{eventName}}",
|
||||
"archive_restored": "Archivo restaurado: {{eventName}}",
|
||||
|
||||
@@ -1567,6 +1567,10 @@
|
||||
"archiveDownloaded": "Archive téléchargée pour \"{{eventName}}\"",
|
||||
"archiveDeleted": "Archive supprimée pour \"{{eventName}}\"",
|
||||
"archiveRestored": "Archive restaurée pour \"{{eventName}}\"",
|
||||
"galleryOpened": "Un invité a ouvert la galerie « {{eventName}} »",
|
||||
"galleryDownloaded": "Un invité a téléchargé des photos de « {{eventName}} »",
|
||||
"galleryDownloadedCustomer": "Le client a téléchargé des photos de « {{eventName}} »",
|
||||
"photoFavorite": "Une photo de « {{eventName}} » a été mise en favori",
|
||||
"systemActivity": "Activité système : {{type}}",
|
||||
"adminProfileUpdated": "Profil administrateur mis à jour par {{actorName}}",
|
||||
"photosUploaded_one": "{{count}} photo téléversée dans \"{{eventName}}\"",
|
||||
@@ -1719,6 +1723,8 @@
|
||||
"analytics": "Analytique",
|
||||
"activities": {
|
||||
"event_created": "Nouvel événement créé : {{eventName}}",
|
||||
"gallery_opened": "La galerie « {{eventName}} » a été ouverte",
|
||||
"gallery_downloaded": "Des photos de « {{eventName}} » ont été téléchargées",
|
||||
"photos_uploaded": "{{count}} photos téléversées dans {{eventName}}",
|
||||
"event_archived": "Événement archivé : {{eventName}}",
|
||||
"archive_restored": "Archive restaurée : {{eventName}}",
|
||||
|
||||
@@ -1556,6 +1556,10 @@
|
||||
"archiveDownloaded": "Archief gedownload voor \"{{eventName}}\"",
|
||||
"archiveDeleted": "Archief verwijderd voor \"{{eventName}}\"",
|
||||
"archiveRestored": "Archief hersteld voor \"{{eventName}}\"",
|
||||
"galleryOpened": "Een gast heeft de galerij “{{eventName}}” geopend",
|
||||
"galleryDownloaded": "Een gast heeft foto’s uit “{{eventName}}” gedownload",
|
||||
"galleryDownloadedCustomer": "Klant heeft foto’s uit “{{eventName}}” gedownload",
|
||||
"photoFavorite": "Een foto in “{{eventName}}” is als favoriet gemarkeerd",
|
||||
"systemActivity": "Systeemactiviteit: {{type}}",
|
||||
"adminProfileUpdated": "Beheerdersprofiel bijgewerkt door {{actorName}}",
|
||||
"photosUploaded_one": "{{count}} foto geüpload naar \"{{eventName}}\"",
|
||||
@@ -1708,6 +1712,8 @@
|
||||
"analytics": "Analyse",
|
||||
"activities": {
|
||||
"event_created": "Nieuw evenement aangemaakt: {{eventName}}",
|
||||
"gallery_opened": "Galerij “{{eventName}}” is geopend",
|
||||
"gallery_downloaded": "Foto’s uit “{{eventName}}” zijn gedownload",
|
||||
"photos_uploaded": "{{count}} foto's geüpload naar {{eventName}}",
|
||||
"event_archived": "Evenement gearchiveerd: {{eventName}}",
|
||||
"archive_restored": "Archief hersteld: {{eventName}}",
|
||||
|
||||
@@ -1573,6 +1573,10 @@
|
||||
"archiveDownloaded": "Arquivo baixado para \"{{eventName}}\"",
|
||||
"archiveDeleted": "Arquivo excluído de \"{{eventName}}\"",
|
||||
"archiveRestored": "Arquivo restaurado para \"{{eventName}}\"",
|
||||
"galleryOpened": "Um convidado abriu a galeria “{{eventName}}”",
|
||||
"galleryDownloaded": "Um convidado baixou fotos de “{{eventName}}”",
|
||||
"galleryDownloadedCustomer": "O cliente baixou fotos de “{{eventName}}”",
|
||||
"photoFavorite": "Uma foto em “{{eventName}}” foi favoritada",
|
||||
"systemActivity": "Atividade do sistema: {{type}}",
|
||||
"adminProfileUpdated": "Perfil admin atualizado por {{actorName}}",
|
||||
"photosUploaded_many": "{{count}} fotos enviadas para \"{{eventName}}\"",
|
||||
@@ -1733,6 +1737,8 @@
|
||||
"analytics": "Análise",
|
||||
"activities": {
|
||||
"event_created": "Novo evento criado: {{eventName}}",
|
||||
"gallery_opened": "A galeria “{{eventName}}” foi aberta",
|
||||
"gallery_downloaded": "Fotos de “{{eventName}}” foram baixadas",
|
||||
"photos_uploaded": "{{count}} fotos carregadas para {{eventName}}",
|
||||
"event_archived": "Evento arquivado: {{eventName}}",
|
||||
"archive_restored": "Arquivo restaurado: {{eventName}}",
|
||||
|
||||
@@ -1590,6 +1590,10 @@
|
||||
"archiveDownloaded": "Скачан архив для «{{eventName}}»",
|
||||
"archiveDeleted": "Архив удалён для «{{eventName}}»",
|
||||
"archiveRestored": "Архив восстановлен для «{{eventName}}»",
|
||||
"galleryOpened": "Гость открыл галерею «{{eventName}}»",
|
||||
"galleryDownloaded": "Гость скачал фотографии из «{{eventName}}»",
|
||||
"galleryDownloadedCustomer": "Клиент скачал фотографии из «{{eventName}}»",
|
||||
"photoFavorite": "Фото из «{{eventName}}» добавлено в избранное",
|
||||
"systemActivity": "Системная активность: {{type}}",
|
||||
"adminProfileUpdated": "Профиль администратора обновлён пользователем {{actorName}}",
|
||||
"photosUploaded_few": "{{count}} фото загружено в «{{eventName}}»",
|
||||
@@ -1758,6 +1762,8 @@
|
||||
"analytics": "Аналитика",
|
||||
"activities": {
|
||||
"event_created": "Новое событие создано: {{eventName}}",
|
||||
"gallery_opened": "Галерея «{{eventName}}» была открыта",
|
||||
"gallery_downloaded": "Фотографии из «{{eventName}}» были скачаны",
|
||||
"photos_uploaded": "{{count}} фото загружено в {{eventName}}",
|
||||
"event_archived": "Событие архивировано: {{eventName}}",
|
||||
"archive_restored": "Архив восстановлен: {{eventName}}",
|
||||
|
||||
@@ -1556,6 +1556,10 @@
|
||||
"archiveDownloaded": "Arhiv prenesen za »{{eventName}}«",
|
||||
"archiveDeleted": "Arhiv izbrisan za »{{eventName}}«",
|
||||
"archiveRestored": "Arhiv obnovljen za »{{eventName}}«",
|
||||
"galleryOpened": "Gost je odprl galerijo »{{eventName}}«",
|
||||
"galleryDownloaded": "Gost je prenesel fotografije iz »{{eventName}}«",
|
||||
"galleryDownloadedCustomer": "Stranka je prenesla fotografije iz »{{eventName}}«",
|
||||
"photoFavorite": "Fotografija v »{{eventName}}« je bila označena kot priljubljena",
|
||||
"systemActivity": "Sistemska aktivnost: {{type}}",
|
||||
"adminProfileUpdated": "Administratorski profil je posodobil {{actorName}}",
|
||||
"photosUploaded_one": "{{count}} fotografija naložena v »{{eventName}}«",
|
||||
@@ -1708,6 +1712,8 @@
|
||||
"analytics": "Analitika",
|
||||
"activities": {
|
||||
"event_created": "Ustvarjen nov dogodek: {{eventName}}",
|
||||
"gallery_opened": "Galerija »{{eventName}}« je bila odprta",
|
||||
"gallery_downloaded": "Fotografije iz »{{eventName}}« so bile prenesene",
|
||||
"photos_uploaded": "{{count}} fotografij naloženih v {{eventName}}",
|
||||
"event_archived": "Dogodek arhiviran: {{eventName}}",
|
||||
"archive_restored": "Arhiv obnovljen: {{eventName}}",
|
||||
|
||||
Reference in New Issue
Block a user