diff --git a/cmd/tier.go b/cmd/tier.go index 64d4594..1c3ede9 100644 --- a/cmd/tier.go +++ b/cmd/tier.go @@ -55,6 +55,7 @@ var cmdTier = &cli.Command{ &cli.DurationFlag{Name: "attachment-expiry-duration", Value: defaultAttachmentExpiryDuration, Usage: "duration after which attachments are deleted"}, &cli.StringFlag{Name: "attachment-bandwidth-limit", Value: defaultAttachmentBandwidthLimit, Usage: "daily bandwidth limit for attachment uploads/downloads"}, &cli.StringFlag{Name: "stripe-price-id", Usage: "Stripe price ID for paid tiers (e.g. price_12345)"}, + &cli.BoolFlag{Name: "ignore-exists", Usage: "if the tier already exists, perform no action and exit"}, }, Description: `Add a new tier to the ntfy user database. @@ -171,6 +172,10 @@ func execTierAdd(c *cli.Context) error { return err } if tier, _ := manager.Tier(code); tier != nil { + if c.Bool("ignore-exists") { + fmt.Fprintf(c.App.ErrWriter, "tier %s already exists (exited successfully)\n", code) + return nil + } return fmt.Errorf("tier %s already exists", code) } name := c.String("name") diff --git a/cmd/user.go b/cmd/user.go index 3440217..a96c708 100644 --- a/cmd/user.go +++ b/cmd/user.go @@ -46,6 +46,7 @@ var cmdUser = &cli.Command{ Action: execUserAdd, Flags: []cli.Flag{ &cli.StringFlag{Name: "role", Aliases: []string{"r"}, Value: string(user.RoleUser), Usage: "user role"}, + &cli.BoolFlag{Name: "ignore-exists", Usage: "if the user already exists, perform no action and exit"}, }, Description: `Add a new user to the ntfy user database. @@ -186,6 +187,10 @@ func execUserAdd(c *cli.Context) error { return err } if user, _ := manager.User(username); user != nil { + if c.Bool("ignore-exists") { + fmt.Fprintf(c.App.ErrWriter, "user %s already exists (exited successfully)\n", username) + return nil + } return fmt.Errorf("user %s already exists", username) } if password == "" { diff --git a/server/server.go b/server/server.go index 11e38cd..6a1ea89 100644 --- a/server/server.go +++ b/server/server.go @@ -313,8 +313,12 @@ func (s *Server) handle(w http.ResponseWriter, r *http.Request) { } func (s *Server) handleError(w http.ResponseWriter, r *http.Request, v *visitor, err error) { + httpErr, ok := err.(*errHTTP) + if !ok { + httpErr = errHTTPInternalError + } + isNormalError := strings.Contains(err.Error(), "i/o timeout") || util.Contains([]int{http.StatusNotFound, http.StatusBadRequest, http.StatusTooManyRequests, http.StatusUnauthorized}, httpErr.HTTPCode) if websocket.IsWebSocketUpgrade(r) { - isNormalError := strings.Contains(err.Error(), "i/o timeout") if isNormalError { logvr(v, r).Tag(tagWebsocket).Err(err).Fields(websocketErrorContext(err)).Debug("WebSocket error (this error is okay, it happens a lot): %s", err.Error()) } else { @@ -323,22 +327,15 @@ func (s *Server) handleError(w http.ResponseWriter, r *http.Request, v *visitor, return // Do not attempt to write to upgraded connection } if matrixErr, ok := err.(*errMatrix); ok { - writeMatrixError(w, r, v, matrixErr) + if err := writeMatrixError(w, r, v, matrixErr); err != nil { + logvr(v, r).Tag(tagMatrix).Err(err).Debug("Writing Matrix error failed") + } return } - httpErr, ok := err.(*errHTTP) - if !ok { - httpErr = errHTTPInternalError - } - isNormalError := httpErr.HTTPCode == http.StatusNotFound || httpErr.HTTPCode == http.StatusBadRequest || httpErr.HTTPCode == http.StatusTooManyRequests if isNormalError { - logvr(v, r). - Err(httpErr). - Debug("Connection closed with HTTP %d (ntfy error %d)", httpErr.HTTPCode, httpErr.Code) + logvr(v, r).Err(httpErr).Debug("Connection closed with HTTP %d (ntfy error %d)", httpErr.HTTPCode, httpErr.Code) } else { - logvr(v, r). - Err(httpErr). - Info("Connection closed with HTTP %d (ntfy error %d)", httpErr.HTTPCode, httpErr.Code) + logvr(v, r).Err(httpErr).Info("Connection closed with HTTP %d (ntfy error %d)", httpErr.HTTPCode, httpErr.Code) } w.Header().Set("Content-Type", "application/json") w.Header().Set("Access-Control-Allow-Origin", s.config.AccessControlAllowOrigin) // CORS, allow cross-origin requests