diff --git a/docs/install.md b/docs/install.md index 4dbfa9ba..53a1368d 100644 --- a/docs/install.md +++ b/docs/install.md @@ -26,37 +26,37 @@ deb/rpm packages. === "x86_64/amd64" ```bash - wget https://github.com/binwiederhier/ntfy/releases/download/v2.0.1/ntfy_2.0.1_linux_x86_64.tar.gz - tar zxvf ntfy_2.0.1_linux_x86_64.tar.gz - sudo cp -a ntfy_2.0.1_linux_x86_64/ntfy /usr/bin/ntfy - sudo mkdir /etc/ntfy && sudo cp ntfy_2.0.1_linux_x86_64/{client,server}/*.yml /etc/ntfy + wget https://github.com/binwiederhier/ntfy/releases/download/v2.1.0/ntfy_2.1.0_linux_x86_64.tar.gz + tar zxvf ntfy_2.1.0_linux_x86_64.tar.gz + sudo cp -a ntfy_2.1.0_linux_x86_64/ntfy /usr/bin/ntfy + sudo mkdir /etc/ntfy && sudo cp ntfy_2.1.0_linux_x86_64/{client,server}/*.yml /etc/ntfy sudo ntfy serve ``` === "armv6" ```bash - wget https://github.com/binwiederhier/ntfy/releases/download/v2.0.1/ntfy_2.0.1_linux_armv6.tar.gz - tar zxvf ntfy_2.0.1_linux_armv6.tar.gz - sudo cp -a ntfy_2.0.1_linux_armv6/ntfy /usr/bin/ntfy - sudo mkdir /etc/ntfy && sudo cp ntfy_2.0.1_linux_armv6/{client,server}/*.yml /etc/ntfy + wget https://github.com/binwiederhier/ntfy/releases/download/v2.1.0/ntfy_2.1.0_linux_armv6.tar.gz + tar zxvf ntfy_2.1.0_linux_armv6.tar.gz + sudo cp -a ntfy_2.1.0_linux_armv6/ntfy /usr/bin/ntfy + sudo mkdir /etc/ntfy && sudo cp ntfy_2.1.0_linux_armv6/{client,server}/*.yml /etc/ntfy sudo ntfy serve ``` === "armv7/armhf" ```bash - wget https://github.com/binwiederhier/ntfy/releases/download/v2.0.1/ntfy_2.0.1_linux_armv7.tar.gz - tar zxvf ntfy_2.0.1_linux_armv7.tar.gz - sudo cp -a ntfy_2.0.1_linux_armv7/ntfy /usr/bin/ntfy - sudo mkdir /etc/ntfy && sudo cp ntfy_2.0.1_linux_armv7/{client,server}/*.yml /etc/ntfy + wget https://github.com/binwiederhier/ntfy/releases/download/v2.1.0/ntfy_2.1.0_linux_armv7.tar.gz + tar zxvf ntfy_2.1.0_linux_armv7.tar.gz + sudo cp -a ntfy_2.1.0_linux_armv7/ntfy /usr/bin/ntfy + sudo mkdir /etc/ntfy && sudo cp ntfy_2.1.0_linux_armv7/{client,server}/*.yml /etc/ntfy sudo ntfy serve ``` === "arm64" ```bash - wget https://github.com/binwiederhier/ntfy/releases/download/v2.0.1/ntfy_2.0.1_linux_arm64.tar.gz - tar zxvf ntfy_2.0.1_linux_arm64.tar.gz - sudo cp -a ntfy_2.0.1_linux_arm64/ntfy /usr/bin/ntfy - sudo mkdir /etc/ntfy && sudo cp ntfy_2.0.1_linux_arm64/{client,server}/*.yml /etc/ntfy + wget https://github.com/binwiederhier/ntfy/releases/download/v2.1.0/ntfy_2.1.0_linux_arm64.tar.gz + tar zxvf ntfy_2.1.0_linux_arm64.tar.gz + sudo cp -a ntfy_2.1.0_linux_arm64/ntfy /usr/bin/ntfy + sudo mkdir /etc/ntfy && sudo cp ntfy_2.1.0_linux_arm64/{client,server}/*.yml /etc/ntfy sudo ntfy serve ``` @@ -106,7 +106,7 @@ Manually installing the .deb file: === "x86_64/amd64" ```bash - wget https://github.com/binwiederhier/ntfy/releases/download/v2.0.1/ntfy_2.0.1_linux_amd64.deb + wget https://github.com/binwiederhier/ntfy/releases/download/v2.1.0/ntfy_2.1.0_linux_amd64.deb sudo dpkg -i ntfy_*.deb sudo systemctl enable ntfy sudo systemctl start ntfy @@ -114,7 +114,7 @@ Manually installing the .deb file: === "armv6" ```bash - wget https://github.com/binwiederhier/ntfy/releases/download/v2.0.1/ntfy_2.0.1_linux_armv6.deb + wget https://github.com/binwiederhier/ntfy/releases/download/v2.1.0/ntfy_2.1.0_linux_armv6.deb sudo dpkg -i ntfy_*.deb sudo systemctl enable ntfy sudo systemctl start ntfy @@ -122,7 +122,7 @@ Manually installing the .deb file: === "armv7/armhf" ```bash - wget https://github.com/binwiederhier/ntfy/releases/download/v2.0.1/ntfy_2.0.1_linux_armv7.deb + wget https://github.com/binwiederhier/ntfy/releases/download/v2.1.0/ntfy_2.1.0_linux_armv7.deb sudo dpkg -i ntfy_*.deb sudo systemctl enable ntfy sudo systemctl start ntfy @@ -130,7 +130,7 @@ Manually installing the .deb file: === "arm64" ```bash - wget https://github.com/binwiederhier/ntfy/releases/download/v2.0.1/ntfy_2.0.1_linux_arm64.deb + wget https://github.com/binwiederhier/ntfy/releases/download/v2.1.0/ntfy_2.1.0_linux_arm64.deb sudo dpkg -i ntfy_*.deb sudo systemctl enable ntfy sudo systemctl start ntfy @@ -140,28 +140,28 @@ Manually installing the .deb file: === "x86_64/amd64" ```bash - sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.0.1/ntfy_2.0.1_linux_amd64.rpm + sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.1.0/ntfy_2.1.0_linux_amd64.rpm sudo systemctl enable ntfy sudo systemctl start ntfy ``` === "armv6" ```bash - sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.0.1/ntfy_2.0.1_linux_armv6.rpm + sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.1.0/ntfy_2.1.0_linux_armv6.rpm sudo systemctl enable ntfy sudo systemctl start ntfy ``` === "armv7/armhf" ```bash - sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.0.1/ntfy_2.0.1_linux_armv7.rpm + sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.1.0/ntfy_2.1.0_linux_armv7.rpm sudo systemctl enable ntfy sudo systemctl start ntfy ``` === "arm64" ```bash - sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.0.1/ntfy_2.0.1_linux_arm64.rpm + sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.1.0/ntfy_2.1.0_linux_arm64.rpm sudo systemctl enable ntfy sudo systemctl start ntfy ``` @@ -189,18 +189,18 @@ NixOS also supports [declarative setup of the ntfy server](https://search.nixos. ## macOS The [ntfy CLI](subscribe/cli.md) (`ntfy publish` and `ntfy subscribe` only) is supported on macOS as well. -To install, please [download the tarball](https://github.com/binwiederhier/ntfy/releases/download/v2.0.1/ntfy_2.0.1_macOS_all.tar.gz), +To install, please [download the tarball](https://github.com/binwiederhier/ntfy/releases/download/v2.1.0/ntfy_2.1.0_macOS_all.tar.gz), extract it and place it somewhere in your `PATH` (e.g. `/usr/local/bin/ntfy`). If run as `root`, ntfy will look for its config at `/etc/ntfy/client.yml`. For all other users, it'll look for it at `~/Library/Application Support/ntfy/client.yml` (sample included in the tarball). ```bash -curl -L https://github.com/binwiederhier/ntfy/releases/download/v2.0.1/ntfy_2.0.1_macOS_all.tar.gz > ntfy_2.0.1_macOS_all.tar.gz -tar zxvf ntfy_2.0.1_macOS_all.tar.gz -sudo cp -a ntfy_2.0.1_macOS_all/ntfy /usr/local/bin/ntfy +curl -L https://github.com/binwiederhier/ntfy/releases/download/v2.1.0/ntfy_2.1.0_macOS_all.tar.gz > ntfy_2.1.0_macOS_all.tar.gz +tar zxvf ntfy_2.1.0_macOS_all.tar.gz +sudo cp -a ntfy_2.1.0_macOS_all/ntfy /usr/local/bin/ntfy mkdir ~/Library/Application\ Support/ntfy -cp ntfy_2.0.1_macOS_all/client/client.yml ~/Library/Application\ Support/ntfy/client.yml +cp ntfy_2.1.0_macOS_all/client/client.yml ~/Library/Application\ Support/ntfy/client.yml ntfy --help ``` @@ -212,7 +212,7 @@ ntfy --help ## Windows The [ntfy CLI](subscribe/cli.md) (`ntfy publish` and `ntfy subscribe` only) is supported on Windows as well. -To install, please [download the latest ZIP](https://github.com/binwiederhier/ntfy/releases/download/v2.0.1/ntfy_2.0.1_windows_x86_64.zip), +To install, please [download the latest ZIP](https://github.com/binwiederhier/ntfy/releases/download/v2.1.0/ntfy_2.1.0_windows_x86_64.zip), extract it and place the `ntfy.exe` binary somewhere in your `%Path%`. The default path for the client config file is at `%AppData%\ntfy\client.yml` (not created automatically, sample in the ZIP file). diff --git a/docs/releases.md b/docs/releases.md index b6f267e4..5c7fda91 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -2,7 +2,8 @@ Binaries for all releases can be found on the GitHub releases pages for the [ntfy server](https://github.com/binwiederhier/ntfy/releases) and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/releases). -## ntfy server v2.1.0 (UNRELEASED) +## ntfy server v2.1.0 +Released February 25, 2023 This release changes the way UnifiedPush (UP) topics are rate limited from publisher-based rate limiting to subscriber-based rate limiting. This allows UP application servers to send higher volumes, since the subscribers carry the rate limits. @@ -13,11 +14,11 @@ We also fixed another issue with UnifiedPush: Some Mastodon servers were sending which ntfy rejected with an HTTP 401. We now ignore unsupported header values. As of this release, ntfy also supports sending emails to protected topics, and it ships code to support annual billing -cycles (not live yet). +cycles (not live yet). **Features:** -* UnifiedPush: Subscriber-based rate limiting for `up*` topics ([#584](https://github.com/binwiederhier/ntfy/pull/584)/[#609](https://github.com/binwiederhier/ntfy/pull/609)/[#633](https://github.com/binwiederhier/ntfy/pull/633), thanks to [@karmanyaahm](https://github.com/karmanyaahm))) +* UnifiedPush: Subscriber-based rate limiting for `up*` topics ([#584](https://github.com/binwiederhier/ntfy/pull/584)/[#609](https://github.com/binwiederhier/ntfy/pull/609)/[#633](https://github.com/binwiederhier/ntfy/pull/633), thanks to [@karmanyaahm](https://github.com/karmanyaahm)) * Support for publishing to protected topics via email with access tokens ([#612](https://github.com/binwiederhier/ntfy/pull/621), thanks to [@tamcore](https://github.com/tamcore)) * Support for base64-encoded and nested multipart emails ([#610](https://github.com/binwiederhier/ntfy/issues/610), thanks to [@Robert-litts](https://github.com/Robert-litts)) * Payments: Add support for annual billing intervals (no ticket) diff --git a/log/event.go b/log/event.go index 55c43091..1e99b7bf 100644 --- a/log/event.go +++ b/log/event.go @@ -119,12 +119,12 @@ func (e *Event) Fields(fields Context) *Event { return e } -// With adds the fields of the given Contexter structs to the log event by calling their With method -func (e *Event) With(contexts ...Contexter) *Event { +// With adds the fields of the given Contexter structs to the log event by calling their Context method +func (e *Event) With(contexters ...Contexter) *Event { if e.contexters == nil { - e.contexters = contexts + e.contexters = contexters } else { - e.contexters = append(e.contexters, contexts...) + e.contexters = append(e.contexters, contexters...) } return e } diff --git a/server/server.go b/server/server.go index ba1ca30c..30e8105e 100644 --- a/server/server.go +++ b/server/server.go @@ -573,9 +573,9 @@ func (s *Server) handlePublishWithoutResponse(r *http.Request, v *visitor) (*mes return nil, err } m := newDefaultMessage(t.ID, "") - cache, firebase, email, unifiedpush, err := s.parsePublishParams(r, m) - if err != nil { - return nil, err + cache, firebase, email, unifiedpush, e := s.parsePublishParams(r, m) + if e != nil { + return nil, e.With(t) } if unifiedpush && t.RateVisitor() == nil { // UnifiedPush clients must subscribe before publishing to allow proper subscriber-based rate limiting (see @@ -607,12 +607,10 @@ func (s *Server) handlePublishWithoutResponse(r *http.Request, v *visitor) (*mes Tag(tagPublish). With(t). Fields(log.Context{ - "message_delayed": delayed, - "message_firebase": firebase, - "message_unifiedpush": unifiedpush, - "message_email": email, - "rate_visitor_ip": vrate.IP().String(), - "rate_visitor_user_id": vrate.MaybeUserID(), + "message_delayed": delayed, + "message_firebase": firebase, + "message_unifiedpush": unifiedpush, + "message_email": email, }) if ev.IsTrace() { ev.Field("message_body", util.MaybeMarshalJSON(m)).Trace("Received message") @@ -709,7 +707,7 @@ func (s *Server) forwardPollRequest(v *visitor, m *message) { } } -func (s *Server) parsePublishParams(r *http.Request, m *message) (cache bool, firebase bool, email string, unifiedpush bool, err error) { +func (s *Server) parsePublishParams(r *http.Request, m *message) (cache bool, firebase bool, email string, unifiedpush bool, err *errHTTP) { cache = readBoolParam(r, true, "x-cache", "cache") firebase = readBoolParam(r, true, "x-firebase", "firebase") m.Title = readParam(r, "x-title", "title", "t") @@ -755,8 +753,9 @@ func (s *Server) parsePublishParams(r *http.Request, m *message) (cache bool, fi if messageStr != "" { m.Message = messageStr } - m.Priority, err = util.ParsePriority(readParam(r, "x-priority", "priority", "prio", "p")) - if err != nil { + var e error + m.Priority, e = util.ParsePriority(readParam(r, "x-priority", "priority", "prio", "p")) + if e != nil { return false, false, "", false, errHTTPBadRequestPriorityInvalid } m.Tags = readCommaSeparatedParam(r, "x-tags", "tags", "tag", "ta") @@ -780,9 +779,9 @@ func (s *Server) parsePublishParams(r *http.Request, m *message) (cache bool, fi } actionsStr := readParam(r, "x-actions", "actions", "action") if actionsStr != "" { - m.Actions, err = parseActions(actionsStr) - if err != nil { - return false, false, "", false, errHTTPBadRequestActionsInvalid.Wrap(err.Error()) + m.Actions, e = parseActions(actionsStr) + if e != nil { + return false, false, "", false, errHTTPBadRequestActionsInvalid.Wrap(e.Error()) } } unifiedpush = readBoolParam(r, false, "x-unifiedpush", "unifiedpush", "up") // see GET too! diff --git a/server/server_manager.go b/server/server_manager.go index 42bdfb39..04e3f327 100644 --- a/server/server_manager.go +++ b/server/server_manager.go @@ -35,17 +35,7 @@ func (s *Server) execManager() { defer s.mu.Unlock() for _, t := range s.topics { subs := t.SubscribersCount() - ev := log.Tag(tagManager) - if ev.IsTrace() { - vrate := t.RateVisitor() - if vrate != nil { - ev.Fields(log.Context{ - "rate_visitor_ip": vrate.IP(), - "rate_visitor_user_id": vrate.MaybeUserID(), - }) - } - ev.With(t).Trace("- topic %s: %d subscribers", t.ID, subs) - } + log.Tag(tagManager).With(t).Trace("- topic %s: %d subscribers", t.ID, subs) msgs, exists := messageCounts[t.ID] if t.Stale() && (!exists || msgs == 0) { log.Tag(tagManager).With(t).Trace("Deleting empty topic %s", t.ID) diff --git a/server/topic.go b/server/topic.go index bb939b66..d3637f83 100644 --- a/server/topic.go +++ b/server/topic.go @@ -133,8 +133,9 @@ func (t *topic) Context() log.Context { "topic_subscribers": len(t.subscribers), } if t.rateVisitor != nil { - fields["topic_rate_visitor_ip"] = t.rateVisitor.IP().String() - fields["topic_rate_visitor_user_id"] = t.rateVisitor.MaybeUserID() + for k, v := range t.rateVisitor.Context() { + fields["topic_rate_"+k] = v + } } return fields }