diff --git a/docs/releases.md b/docs/releases.md index 4131a89..e4f78db 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -13,6 +13,10 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release ## ntfy server v1.25.0 (UNRELEASED) +**Bugs**: + +* Respect Firebase "quota exceeded" response for topics, block Firebase publishing for user for 10min ([#289](https://github.com/binwiederhier/ntfy/issues/289)) + **Maintenance:** * Upgrade Firebase Admin SDK to 4.x ([#274](https://github.com/binwiederhier/ntfy/issues/274)) diff --git a/server/config.go b/server/config.go index 7f15aed..60d3cdf 100644 --- a/server/config.go +++ b/server/config.go @@ -6,16 +6,16 @@ import ( // Defines default config settings (excluding limits, see below) const ( - DefaultListenHTTP = ":80" - DefaultCacheDuration = 12 * time.Hour - DefaultKeepaliveInterval = 45 * time.Second // Not too frequently to save battery (Android read timeout used to be 77s!) - DefaultManagerInterval = time.Minute - DefaultAtSenderInterval = 10 * time.Second - DefaultMinDelay = 10 * time.Second - DefaultMaxDelay = 3 * 24 * time.Hour - DefaultFirebaseKeepaliveInterval = 3 * time.Hour // ~control topic (Android), not too frequently to save battery - DefaultFirebasePollInterval = 20 * time.Minute // ~poll topic (iOS), max. 2-3 times per hour (see docs) - DefaultFirebaseQuotaLimitPenaltyDuration = 10 * time.Minute + DefaultListenHTTP = ":80" + DefaultCacheDuration = 12 * time.Hour + DefaultKeepaliveInterval = 45 * time.Second // Not too frequently to save battery (Android read timeout used to be 77s!) + DefaultManagerInterval = time.Minute + DefaultDelayedSenderInterval = 10 * time.Second + DefaultMinDelay = 10 * time.Second + DefaultMaxDelay = 3 * 24 * time.Hour + DefaultFirebaseKeepaliveInterval = 3 * time.Hour // ~control topic (Android), not too frequently to save battery + DefaultFirebasePollInterval = 20 * time.Minute // ~poll topic (iOS), max. 2-3 times per hour (see docs) + DefaultFirebaseQuotaExceededPenaltyDuration = 10 * time.Minute // Time that over-users are locked out of Firebase if it returns "quota exceeded" ) // Defines all global and per-visitor limits @@ -70,7 +70,7 @@ type Config struct { DelayedSenderInterval time.Duration FirebaseKeepaliveInterval time.Duration FirebasePollInterval time.Duration - FirebaseQuotaLimitPenaltyDuration time.Duration + FirebaseQuotaExceededPenaltyDuration time.Duration UpstreamBaseURL string SMTPSenderAddr string SMTPSenderUser string @@ -120,10 +120,10 @@ func NewConfig() *Config { MessageLimit: DefaultMessageLengthLimit, MinDelay: DefaultMinDelay, MaxDelay: DefaultMaxDelay, - DelayedSenderInterval: DefaultAtSenderInterval, + DelayedSenderInterval: DefaultDelayedSenderInterval, FirebaseKeepaliveInterval: DefaultFirebaseKeepaliveInterval, FirebasePollInterval: DefaultFirebasePollInterval, - FirebaseQuotaLimitPenaltyDuration: DefaultFirebaseQuotaLimitPenaltyDuration, + FirebaseQuotaExceededPenaltyDuration: DefaultFirebaseQuotaExceededPenaltyDuration, TotalTopicLimit: DefaultTotalTopicLimit, VisitorSubscriptionLimit: DefaultVisitorSubscriptionLimit, VisitorAttachmentTotalSizeLimit: DefaultVisitorAttachmentTotalSizeLimit, diff --git a/server/errors.go b/server/errors.go index 2fa883f..32c1b3b 100644 --- a/server/errors.go +++ b/server/errors.go @@ -59,7 +59,6 @@ var ( errHTTPTooManyRequestsLimitSubscriptions = &errHTTP{42903, http.StatusTooManyRequests, "limit reached: too many active subscriptions, please be nice", "https://ntfy.sh/docs/publish/#limitations"} errHTTPTooManyRequestsLimitTotalTopics = &errHTTP{42904, http.StatusTooManyRequests, "limit reached: the total number of topics on the server has been reached, please contact the admin", "https://ntfy.sh/docs/publish/#limitations"} errHTTPTooManyRequestsAttachmentBandwidthLimit = &errHTTP{42905, http.StatusTooManyRequests, "too many requests: daily bandwidth limit reached", "https://ntfy.sh/docs/publish/#limitations"} - errHTTPTooManyRequestsFirebaseQuotaReached = &errHTTP{42906, http.StatusTooManyRequests, "too many requests: Firebase quota for topic reached", "https://ntfy.sh/docs/publish/#limitations"} errHTTPInternalError = &errHTTP{50001, http.StatusInternalServerError, "internal server error", ""} errHTTPInternalErrorInvalidFilePath = &errHTTP{50002, http.StatusInternalServerError, "internal server error: invalid file path", ""} ) diff --git a/server/smtp_server.go b/server/smtp_server.go index a5b6f85..7812371 100644 --- a/server/smtp_server.go +++ b/server/smtp_server.go @@ -146,6 +146,7 @@ func (s *smtpSession) publishMessage(m *message) error { url := fmt.Sprintf("%s/%s", s.backend.config.BaseURL, m.Topic) req, err := http.NewRequest("PUT", url, strings.NewReader(m.Message)) req.RemoteAddr = s.remoteAddr // rate limiting!! + req.Header.Set("X-Forwarded-For", s.remoteAddr) if err != nil { return err } diff --git a/server/visitor.go b/server/visitor.go index 1bbc4e0..5a8e186 100644 --- a/server/visitor.go +++ b/server/visitor.go @@ -73,7 +73,7 @@ func (v *visitor) FirebaseAllowed() error { func (v *visitor) FirebaseTemporarilyDeny() { v.mu.Lock() defer v.mu.Unlock() - v.firebase = time.Now().Add(v.config.FirebaseQuotaLimitPenaltyDuration) + v.firebase = time.Now().Add(v.config.FirebaseQuotaExceededPenaltyDuration) } func (v *visitor) EmailAllowed() error {