diff --git a/docs/releases.md b/docs/releases.md index 1d80a039..2f3f669e 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -1603,22 +1603,28 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release **Features:** -* Support for [updating and deleting notifications](publish.md#updating-deleting-notifications): You can now update, - clear (mark as read), or delete notifications using a sequence ID. This enables use cases like progress updates, - replacing outdated notifications, or dismissing notifications from all clients. +* Support for [updating and deleting notifications](publish.md#updating-deleting-notifications) + ([#303](https://github.com/binwiederhier/ntfy/issues/303), [#1536](https://github.com/binwiederhier/ntfy/pull/1536), + [ntfy-android#151](https://github.com/binwiederhier/ntfy-android/pull/151), thanks to [@wunter8](https://github.com/wunter8) + for the initial implementation) ### ntfy Android app v1.22.x (UNRELEASED) **Features:** -* Support for self-signed certs and client certs for mTLS (#215, #530, ntfy-android#149) +* Support for [updating and deleting notifications](publish.md#updating-deleting-notifications) + ([#303](https://github.com/binwiederhier/ntfy/issues/303), [#1536](https://github.com/binwiederhier/ntfy/pull/1536), + [ntfy-android#151](https://github.com/binwiederhier/ntfy-android/pull/151), thanks to [@wunter8](https://github.com/wunter8) + for the initial implementation) +* Support for self-signed certs and client certs for mTLS ([#215](https://github.com/binwiederhier/ntfy/issues/215), + [#530](https://github.com/binwiederhier/ntfy/issues/530), [ntfy-android#149](https://github.com/binwiederhier/ntfy-android/pull/149), + thanks to [@cyb3rko](https://github.com/cyb3rko) for reviewing) * Connection error dialog to help diagnose connection issues -* Support for [updating and deleting notifications](publish.md#updating-deleting-notifications): Notifications with - the same sequence ID are updated in place, and `message_delete`/`message_clear` events dismiss notifications **Bug fixes + maintenance:** -* Use server-specific user for attachment downloads (#1529, thanks to @ManInDark for reporting) -* Fix crash in sharing dialog (thanks to @rogeliodh) +* Use server-specific user for attachment downloads ([#1529](https://github.com/binwiederhier/ntfy/issues/1529), + thanks to [@ManInDark](https://github.com/ManInDark) for reporting and testing) +* Fix crash in sharing dialog (thanks to [@rogeliodh](https://github.com/rogeliodh)) * Fix crash when exiting multi-delete in detail view * Fix potential crashes with icon downloader and backuper diff --git a/server/errors.go b/server/errors.go index e8f58d75..a29ff27d 100644 --- a/server/errors.go +++ b/server/errors.go @@ -3,8 +3,9 @@ package server import ( "encoding/json" "fmt" - "heckel.io/ntfy/v2/log" "net/http" + + "heckel.io/ntfy/v2/log" ) // errHTTP is a generic HTTP error for any non-200 HTTP error @@ -125,7 +126,7 @@ var ( errHTTPBadRequestInvalidUsername = &errHTTP{40046, http.StatusBadRequest, "invalid request: invalid username", "", nil} errHTTPBadRequestTemplateFileNotFound = &errHTTP{40047, http.StatusBadRequest, "invalid request: template file not found", "https://ntfy.sh/docs/publish/#message-templating", nil} errHTTPBadRequestTemplateFileInvalid = &errHTTP{40048, http.StatusBadRequest, "invalid request: template file invalid", "https://ntfy.sh/docs/publish/#message-templating", nil} - errHTTPBadRequestSequenceIDInvalid = &errHTTP{40049, http.StatusBadRequest, "invalid request: sequence ID invalid", "https://ntfy.sh/docs/publish/#TODO", nil} + errHTTPBadRequestSequenceIDInvalid = &errHTTP{40049, http.StatusBadRequest, "invalid request: sequence ID invalid", "https://ntfy.sh/docs/publish/#updating-deleting-notifications", nil} errHTTPNotFound = &errHTTP{40401, http.StatusNotFound, "page not found", "", nil} errHTTPUnauthorized = &errHTTP{40101, http.StatusUnauthorized, "unauthorized", "https://ntfy.sh/docs/publish/#authentication", nil} errHTTPForbidden = &errHTTP{40301, http.StatusForbidden, "forbidden", "https://ntfy.sh/docs/publish/#authentication", nil} diff --git a/web/public/sw.js b/web/public/sw.js index 7affd55f..6b298414 100644 --- a/web/public/sw.js +++ b/web/public/sw.js @@ -6,7 +6,13 @@ import { clientsClaim } from "workbox-core"; import { dbAsync } from "../src/app/db"; import { badge, icon, messageWithSequenceId, toNotificationParams } from "../src/app/notificationUtils"; import initI18n from "../src/app/i18n"; -import { EVENT_MESSAGE, EVENT_MESSAGE_CLEAR, EVENT_MESSAGE_DELETE, EVENT_SUBSCRIPTION_EXPIRING } from "../src/app/events"; +import { + EVENT_MESSAGE, + EVENT_MESSAGE_CLEAR, + EVENT_MESSAGE_DELETE, + WEBPUSH_EVENT_MESSAGE, + WEBPUSH_EVENT_SUBSCRIPTION_EXPIRING, +} from "../src/app/events"; /** * General docs for service workers and PWAs: @@ -161,25 +167,26 @@ const handlePushUnknown = async (data) => { * @param {object} data see server/types.go, type webPushPayload */ const handlePush = async (data) => { - const { message } = data; - // This logic is (partially) duplicated in // - Android: SubscriberService::onNotificationReceived() // - Android: FirebaseService::onMessageReceived() // - Web app: hooks.js:handleNotification() // - Web app: sw.js:handleMessage(), sw.js:handleMessageClear(), ... - if (message.event === EVENT_MESSAGE) { - await handlePushMessage(data); - } else if (message.event === EVENT_MESSAGE_DELETE) { - await handlePushMessageDelete(data); - } else if (message.event === EVENT_MESSAGE_CLEAR) { - await handlePushMessageClear(data); - } else if (message.event === EVENT_SUBSCRIPTION_EXPIRING) { - await handlePushSubscriptionExpiring(data); - } else { - await handlePushUnknown(data); + if (data.event === WEBPUSH_EVENT_MESSAGE) { + const { message } = data; + if (message.event === EVENT_MESSAGE) { + return await handlePushMessage(data); + } else if (message.event === EVENT_MESSAGE_DELETE) { + return await handlePushMessageDelete(data); + } else if (message.event === EVENT_MESSAGE_CLEAR) { + return await handlePushMessageClear(data); + } + } else if (data.event === WEBPUSH_EVENT_SUBSCRIPTION_EXPIRING) { + return await handlePushSubscriptionExpiring(data); } + + return await handlePushUnknown(data); }; /** diff --git a/web/src/app/events.js b/web/src/app/events.js index 94d7dc79..d5c5ab88 100644 --- a/web/src/app/events.js +++ b/web/src/app/events.js @@ -7,7 +7,9 @@ export const EVENT_MESSAGE = "message"; export const EVENT_MESSAGE_DELETE = "message_delete"; export const EVENT_MESSAGE_CLEAR = "message_clear"; export const EVENT_POLL_REQUEST = "poll_request"; -export const EVENT_SUBSCRIPTION_EXPIRING = "subscription_expiring"; + +export const WEBPUSH_EVENT_MESSAGE = "message"; +export const WEBPUSH_EVENT_SUBSCRIPTION_EXPIRING = "subscription_expiring"; // Check if an event is a notification event (message, delete, or read) export const isNotificationEvent = (event) => event === EVENT_MESSAGE || event === EVENT_MESSAGE_DELETE || event === EVENT_MESSAGE_CLEAR;