Fine tuning
This commit is contained in:
parent
797e4640df
commit
c684a39191
4 changed files with 35 additions and 26 deletions
|
@ -38,7 +38,7 @@ var flagsServe = []cli.Flag{
|
||||||
altsrc.NewDurationFlag(&cli.DurationFlag{Name: "attachment-expiry-duration", Aliases: []string{"X"}, EnvVars: []string{"NTFY_ATTACHMENT_EXPIRY_DURATION"}, Value: server.DefaultAttachmentExpiryDuration, DefaultText: "3h", Usage: "duration after which uploaded attachments will be deleted (e.g. 3h, 20h)"}),
|
altsrc.NewDurationFlag(&cli.DurationFlag{Name: "attachment-expiry-duration", Aliases: []string{"X"}, EnvVars: []string{"NTFY_ATTACHMENT_EXPIRY_DURATION"}, Value: server.DefaultAttachmentExpiryDuration, DefaultText: "3h", Usage: "duration after which uploaded attachments will be deleted (e.g. 3h, 20h)"}),
|
||||||
altsrc.NewDurationFlag(&cli.DurationFlag{Name: "keepalive-interval", Aliases: []string{"k"}, EnvVars: []string{"NTFY_KEEPALIVE_INTERVAL"}, Value: server.DefaultKeepaliveInterval, Usage: "interval of keepalive messages"}),
|
altsrc.NewDurationFlag(&cli.DurationFlag{Name: "keepalive-interval", Aliases: []string{"k"}, EnvVars: []string{"NTFY_KEEPALIVE_INTERVAL"}, Value: server.DefaultKeepaliveInterval, Usage: "interval of keepalive messages"}),
|
||||||
altsrc.NewDurationFlag(&cli.DurationFlag{Name: "manager-interval", Aliases: []string{"m"}, EnvVars: []string{"NTFY_MANAGER_INTERVAL"}, Value: server.DefaultManagerInterval, Usage: "interval of for message pruning and stats printing"}),
|
altsrc.NewDurationFlag(&cli.DurationFlag{Name: "manager-interval", Aliases: []string{"m"}, EnvVars: []string{"NTFY_MANAGER_INTERVAL"}, Value: server.DefaultManagerInterval, Usage: "interval of for message pruning and stats printing"}),
|
||||||
altsrc.NewStringFlag(&cli.StringFlag{Name: "web-root", EnvVars: []string{"NTFY_WEB_ROOT"}, Value: "app", Usage: "sets web root to landing page (home) or web app (app)"}),
|
altsrc.NewStringFlag(&cli.StringFlag{Name: "web-root", EnvVars: []string{"NTFY_WEB_ROOT"}, Value: "app", Usage: "sets web root to landing page (home), web app (app) or disabled (disable)"}),
|
||||||
altsrc.NewStringFlag(&cli.StringFlag{Name: "smtp-sender-addr", EnvVars: []string{"NTFY_SMTP_SENDER_ADDR"}, Usage: "SMTP server address (host:port) for outgoing emails"}),
|
altsrc.NewStringFlag(&cli.StringFlag{Name: "smtp-sender-addr", EnvVars: []string{"NTFY_SMTP_SENDER_ADDR"}, Usage: "SMTP server address (host:port) for outgoing emails"}),
|
||||||
altsrc.NewStringFlag(&cli.StringFlag{Name: "smtp-sender-user", EnvVars: []string{"NTFY_SMTP_SENDER_USER"}, Usage: "SMTP user (if e-mail sending is enabled)"}),
|
altsrc.NewStringFlag(&cli.StringFlag{Name: "smtp-sender-user", EnvVars: []string{"NTFY_SMTP_SENDER_USER"}, Usage: "SMTP user (if e-mail sending is enabled)"}),
|
||||||
altsrc.NewStringFlag(&cli.StringFlag{Name: "smtp-sender-pass", EnvVars: []string{"NTFY_SMTP_SENDER_PASS"}, Usage: "SMTP password (if e-mail sending is enabled)"}),
|
altsrc.NewStringFlag(&cli.StringFlag{Name: "smtp-sender-pass", EnvVars: []string{"NTFY_SMTP_SENDER_PASS"}, Usage: "SMTP password (if e-mail sending is enabled)"}),
|
||||||
|
@ -143,7 +143,7 @@ func execServe(c *cli.Context) error {
|
||||||
return errors.New("if set, base-url must start with http:// or https://")
|
return errors.New("if set, base-url must start with http:// or https://")
|
||||||
} else if !util.InStringList([]string{"read-write", "read-only", "write-only", "deny-all"}, authDefaultAccess) {
|
} else if !util.InStringList([]string{"read-write", "read-only", "write-only", "deny-all"}, authDefaultAccess) {
|
||||||
return errors.New("if set, auth-default-access must start set to 'read-write', 'read-only', 'write-only' or 'deny-all'")
|
return errors.New("if set, auth-default-access must start set to 'read-write', 'read-only', 'write-only' or 'deny-all'")
|
||||||
} else if !util.InStringList([]string{"app", "home"}, webRoot) {
|
} else if !util.InStringList([]string{"app", "home", "disable"}, webRoot) {
|
||||||
return errors.New("if set, web-root must be 'home' or 'app'")
|
return errors.New("if set, web-root must be 'home' or 'app'")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -802,7 +802,7 @@ variable before running the `ntfy` command (e.g. `export NTFY_LISTEN_HTTP=:80`).
|
||||||
| `smtp-server-addr-prefix` | `NTFY_SMTP_SERVER_ADDR_PREFIX` | `[ip]:port` | - | Optional prefix for the e-mail addresses to prevent spam, e.g. `ntfy-` |
|
| `smtp-server-addr-prefix` | `NTFY_SMTP_SERVER_ADDR_PREFIX` | `[ip]:port` | - | Optional prefix for the e-mail addresses to prevent spam, e.g. `ntfy-` |
|
||||||
| `keepalive-interval` | `NTFY_KEEPALIVE_INTERVAL` | *duration* | 45s | Interval in which keepalive messages are sent to the client. This is to prevent intermediaries closing the connection for inactivity. Note that the Android app has a hardcoded timeout at 77s, so it should be less than that. |
|
| `keepalive-interval` | `NTFY_KEEPALIVE_INTERVAL` | *duration* | 45s | Interval in which keepalive messages are sent to the client. This is to prevent intermediaries closing the connection for inactivity. Note that the Android app has a hardcoded timeout at 77s, so it should be less than that. |
|
||||||
| `manager-interval` | `$NTFY_MANAGER_INTERVAL` | *duration* | 1m | Interval in which the manager prunes old messages, deletes topics and prints the stats. |
|
| `manager-interval` | `$NTFY_MANAGER_INTERVAL` | *duration* | 1m | Interval in which the manager prunes old messages, deletes topics and prints the stats. |
|
||||||
| `web-root` | `NTFY_WEB_ROOT` | `app` or `home` | `app` | Sets web root to landing page (home), web app (app) or (disable) for no WebUI. |
|
| `web-root` | `NTFY_WEB_ROOT` | `app`, `home` or `disable` | `app` | Sets web root to landing page (home), web app (app) or disables the web app entirely (disable) |
|
||||||
| `global-topic-limit` | `NTFY_GLOBAL_TOPIC_LIMIT` | *number* | 15,000 | Rate limiting: Total number of topics before the server rejects new topics. |
|
| `global-topic-limit` | `NTFY_GLOBAL_TOPIC_LIMIT` | *number* | 15,000 | Rate limiting: Total number of topics before the server rejects new topics. |
|
||||||
| `visitor-subscription-limit` | `NTFY_VISITOR_SUBSCRIPTION_LIMIT` | *number* | 30 | Rate limiting: Number of subscriptions per visitor (IP address) |
|
| `visitor-subscription-limit` | `NTFY_VISITOR_SUBSCRIPTION_LIMIT` | *number* | 30 | Rate limiting: Number of subscriptions per visitor (IP address) |
|
||||||
| `visitor-attachment-total-size-limit` | `NTFY_VISITOR_ATTACHMENT_TOTAL_SIZE_LIMIT` | *size* | 100M | Rate limiting: Total storage limit used for attachments per visitor, for all attachments combined. Storage is freed after attachments expire. See `attachment-expiry-duration`. |
|
| `visitor-attachment-total-size-limit` | `NTFY_VISITOR_ATTACHMENT_TOTAL_SIZE_LIMIT` | *size* | 100M | Rate limiting: Total storage limit used for attachments per visitor, for all attachments combined. Storage is freed after attachments expire. See `attachment-expiry-duration`. |
|
||||||
|
|
|
@ -263,24 +263,24 @@ func (s *Server) handle(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleInternal(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
func (s *Server) handleInternal(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||||
if r.Method == http.MethodGet && r.URL.Path == "/" && s.config.EnableWeb {
|
if r.Method == http.MethodGet && r.URL.Path == "/" {
|
||||||
return s.handleHome(w, r)
|
return s.ensureWebEnabled(s.handleHome)(w, r, v)
|
||||||
} else if r.Method == http.MethodGet && r.URL.Path == "/example.html" && s.config.EnableWeb {
|
} else if r.Method == http.MethodGet && r.URL.Path == "/example.html" {
|
||||||
return s.handleExample(w, r)
|
return s.ensureWebEnabled(s.handleExample)(w, r, v)
|
||||||
} else if r.Method == http.MethodHead && r.URL.Path == "/" {
|
} else if r.Method == http.MethodHead && r.URL.Path == "/" {
|
||||||
return s.handleEmpty(w, r, v)
|
return s.ensureWebEnabled(s.handleEmpty)(w, r, v)
|
||||||
} else if r.Method == http.MethodGet && r.URL.Path == webConfigPath && s.config.EnableWeb {
|
} else if r.Method == http.MethodGet && r.URL.Path == webConfigPath {
|
||||||
return s.handleWebConfig(w, r)
|
return s.ensureWebEnabled(s.handleWebConfig)(w, r, v)
|
||||||
} else if r.Method == http.MethodGet && r.URL.Path == userStatsPath {
|
} else if r.Method == http.MethodGet && r.URL.Path == userStatsPath {
|
||||||
return s.handleUserStats(w, r, v)
|
return s.handleUserStats(w, r, v)
|
||||||
} else if r.Method == http.MethodGet && staticRegex.MatchString(r.URL.Path) && s.config.EnableWeb {
|
} else if r.Method == http.MethodGet && staticRegex.MatchString(r.URL.Path) {
|
||||||
return s.handleStatic(w, r)
|
return s.ensureWebEnabled(s.handleStatic)(w, r, v)
|
||||||
} else if r.Method == http.MethodGet && docsRegex.MatchString(r.URL.Path) && s.config.EnableWeb {
|
} else if r.Method == http.MethodGet && docsRegex.MatchString(r.URL.Path) {
|
||||||
return s.handleDocs(w, r)
|
return s.ensureWebEnabled(s.handleDocs)(w, r, v)
|
||||||
} else if r.Method == http.MethodGet && fileRegex.MatchString(r.URL.Path) && s.config.AttachmentCacheDir != "" {
|
} else if r.Method == http.MethodGet && fileRegex.MatchString(r.URL.Path) && s.config.AttachmentCacheDir != "" {
|
||||||
return s.limitRequests(s.handleFile)(w, r, v)
|
return s.limitRequests(s.handleFile)(w, r, v)
|
||||||
} else if r.Method == http.MethodOptions {
|
} else if r.Method == http.MethodOptions {
|
||||||
return s.handleOptions(w, r)
|
return s.ensureWebEnabled(s.handleOptions)(w, r, v)
|
||||||
} else if (r.Method == http.MethodPut || r.Method == http.MethodPost) && r.URL.Path == "/" {
|
} else if (r.Method == http.MethodPut || r.Method == http.MethodPost) && r.URL.Path == "/" {
|
||||||
return s.limitRequests(s.transformBodyJSON(s.authWrite(s.handlePublish)))(w, r, v)
|
return s.limitRequests(s.transformBodyJSON(s.authWrite(s.handlePublish)))(w, r, v)
|
||||||
} else if (r.Method == http.MethodPut || r.Method == http.MethodPost) && topicPathRegex.MatchString(r.URL.Path) {
|
} else if (r.Method == http.MethodPut || r.Method == http.MethodPost) && topicPathRegex.MatchString(r.URL.Path) {
|
||||||
|
@ -298,21 +298,21 @@ func (s *Server) handleInternal(w http.ResponseWriter, r *http.Request, v *visit
|
||||||
} else if r.Method == http.MethodGet && authPathRegex.MatchString(r.URL.Path) {
|
} else if r.Method == http.MethodGet && authPathRegex.MatchString(r.URL.Path) {
|
||||||
return s.limitRequests(s.authRead(s.handleTopicAuth))(w, r, v)
|
return s.limitRequests(s.authRead(s.handleTopicAuth))(w, r, v)
|
||||||
} else if r.Method == http.MethodGet && (topicPathRegex.MatchString(r.URL.Path) || externalTopicPathRegex.MatchString(r.URL.Path)) {
|
} else if r.Method == http.MethodGet && (topicPathRegex.MatchString(r.URL.Path) || externalTopicPathRegex.MatchString(r.URL.Path)) {
|
||||||
return s.handleTopic(w, r)
|
return s.ensureWebEnabled(s.handleTopic)(w, r, v)
|
||||||
}
|
}
|
||||||
return errHTTPNotFound
|
return errHTTPNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleHome(w http.ResponseWriter, r *http.Request) error {
|
func (s *Server) handleHome(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||||
if s.config.WebRootIsApp {
|
if s.config.WebRootIsApp {
|
||||||
r.URL.Path = webAppIndex
|
r.URL.Path = webAppIndex
|
||||||
} else {
|
} else {
|
||||||
r.URL.Path = webHomeIndex
|
r.URL.Path = webHomeIndex
|
||||||
}
|
}
|
||||||
return s.handleStatic(w, r)
|
return s.handleStatic(w, r, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleTopic(w http.ResponseWriter, r *http.Request) error {
|
func (s *Server) handleTopic(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||||
unifiedpush := readBoolParam(r, false, "x-unifiedpush", "unifiedpush", "up") // see PUT/POST too!
|
unifiedpush := readBoolParam(r, false, "x-unifiedpush", "unifiedpush", "up") // see PUT/POST too!
|
||||||
if unifiedpush {
|
if unifiedpush {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
@ -321,7 +321,7 @@ func (s *Server) handleTopic(w http.ResponseWriter, r *http.Request) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
r.URL.Path = webAppIndex
|
r.URL.Path = webAppIndex
|
||||||
return s.handleStatic(w, r)
|
return s.handleStatic(w, r, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleEmpty(_ http.ResponseWriter, _ *http.Request, _ *visitor) error {
|
func (s *Server) handleEmpty(_ http.ResponseWriter, _ *http.Request, _ *visitor) error {
|
||||||
|
@ -335,12 +335,12 @@ func (s *Server) handleTopicAuth(w http.ResponseWriter, _ *http.Request, _ *visi
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleExample(w http.ResponseWriter, _ *http.Request) error {
|
func (s *Server) handleExample(w http.ResponseWriter, _ *http.Request, _ *visitor) error {
|
||||||
_, err := io.WriteString(w, exampleSource)
|
_, err := io.WriteString(w, exampleSource)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleWebConfig(w http.ResponseWriter, r *http.Request) error {
|
func (s *Server) handleWebConfig(w http.ResponseWriter, _ *http.Request, _ *visitor) error {
|
||||||
appRoot := "/"
|
appRoot := "/"
|
||||||
if !s.config.WebRootIsApp {
|
if !s.config.WebRootIsApp {
|
||||||
appRoot = "/app"
|
appRoot = "/app"
|
||||||
|
@ -368,13 +368,13 @@ func (s *Server) handleUserStats(w http.ResponseWriter, r *http.Request, v *visi
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleStatic(w http.ResponseWriter, r *http.Request) error {
|
func (s *Server) handleStatic(w http.ResponseWriter, r *http.Request, _ *visitor) error {
|
||||||
r.URL.Path = webSiteDir + r.URL.Path
|
r.URL.Path = webSiteDir + r.URL.Path
|
||||||
util.Gzip(http.FileServer(http.FS(webFsCached))).ServeHTTP(w, r)
|
util.Gzip(http.FileServer(http.FS(webFsCached))).ServeHTTP(w, r)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleDocs(w http.ResponseWriter, r *http.Request) error {
|
func (s *Server) handleDocs(w http.ResponseWriter, r *http.Request, _ *visitor) error {
|
||||||
util.Gzip(http.FileServer(http.FS(docsStaticCached))).ServeHTTP(w, r)
|
util.Gzip(http.FileServer(http.FS(docsStaticCached))).ServeHTTP(w, r)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -905,7 +905,7 @@ func parseSince(r *http.Request, poll bool) (sinceMarker, error) {
|
||||||
return sinceNoMessages, errHTTPBadRequestSinceInvalid
|
return sinceNoMessages, errHTTPBadRequestSinceInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleOptions(w http.ResponseWriter, _ *http.Request) error {
|
func (s *Server) handleOptions(w http.ResponseWriter, _ *http.Request, _ *visitor) error {
|
||||||
w.Header().Set("Access-Control-Allow-Methods", "GET, PUT, POST")
|
w.Header().Set("Access-Control-Allow-Methods", "GET, PUT, POST")
|
||||||
w.Header().Set("Access-Control-Allow-Origin", "*") // CORS, allow cross-origin requests
|
w.Header().Set("Access-Control-Allow-Origin", "*") // CORS, allow cross-origin requests
|
||||||
w.Header().Set("Access-Control-Allow-Headers", "*") // CORS, allow auth via JS // FIXME is this terrible?
|
w.Header().Set("Access-Control-Allow-Headers", "*") // CORS, allow auth via JS // FIXME is this terrible?
|
||||||
|
@ -1119,6 +1119,15 @@ func (s *Server) limitRequests(next handleFunc) handleFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) ensureWebEnabled(next handleFunc) handleFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||||
|
if !s.config.EnableWeb {
|
||||||
|
return errHTTPNotFound
|
||||||
|
}
|
||||||
|
return next(w, r, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// transformBodyJSON peeks the request body, reads the JSON, and converts it to headers
|
// transformBodyJSON peeks the request body, reads the JSON, and converts it to headers
|
||||||
// before passing it on to the next handler. This is meant to be used in combination with handlePublish.
|
// before passing it on to the next handler. This is meant to be used in combination with handlePublish.
|
||||||
func (s *Server) transformBodyJSON(next handleFunc) handleFunc {
|
func (s *Server) transformBodyJSON(next handleFunc) handleFunc {
|
||||||
|
|
|
@ -128,7 +128,7 @@
|
||||||
|
|
||||||
# Defines if the root route (/) is pointing to the landing page (as on ntfy.sh) or the
|
# Defines if the root route (/) is pointing to the landing page (as on ntfy.sh) or the
|
||||||
# web app. If you self-host, you don't want to change this.
|
# web app. If you self-host, you don't want to change this.
|
||||||
# Can be "app" (default), "home" or "disable" to disable the WebUI.
|
# Can be "app" (default), "home" or "disable" to disable the web app entirely.
|
||||||
#
|
#
|
||||||
# web-root: app
|
# web-root: app
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue