Philipp Heckel 2 лет назад
Родитель
Сommit
c684a39191
4 измененных файлов с 35 добавлено и 26 удалено
  1. 2 2
      cmd/serve_linux.go
  2. 1 1
      docs/config.md
  3. 31 22
      server/server.go
  4. 1 1
      server/server.yml

+ 2 - 2
cmd/serve_linux.go

@@ -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: "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.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-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)"}),
@@ -143,7 +143,7 @@ func execServe(c *cli.Context) error {
 		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) {
 		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'")
 	}
 

+ 1 - 1
docs/config.md

@@ -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-`                                                                                                                                                          |
 | `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.                                                                                                                                         |
-| `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.                                                                                                                                                     |
 | `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`.                                                 |

+ 31 - 22
server/server.go

@@ -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 {
-	if r.Method == http.MethodGet && r.URL.Path == "/" && s.config.EnableWeb {
-		return s.handleHome(w, r)
-	} else if r.Method == http.MethodGet && r.URL.Path == "/example.html" && s.config.EnableWeb {
-		return s.handleExample(w, r)
+	if r.Method == http.MethodGet && r.URL.Path == "/" {
+		return s.ensureWebEnabled(s.handleHome)(w, r, v)
+	} else if r.Method == http.MethodGet && r.URL.Path == "/example.html" {
+		return s.ensureWebEnabled(s.handleExample)(w, r, v)
 	} else if r.Method == http.MethodHead && r.URL.Path == "/" {
-		return s.handleEmpty(w, r, v)
-	} else if r.Method == http.MethodGet && r.URL.Path == webConfigPath && s.config.EnableWeb {
-		return s.handleWebConfig(w, r)
+		return s.ensureWebEnabled(s.handleEmpty)(w, r, v)
+	} else if r.Method == http.MethodGet && r.URL.Path == webConfigPath {
+		return s.ensureWebEnabled(s.handleWebConfig)(w, r, v)
 	} else if r.Method == http.MethodGet && r.URL.Path == userStatsPath {
 		return s.handleUserStats(w, r, v)
-	} else if r.Method == http.MethodGet && staticRegex.MatchString(r.URL.Path) && s.config.EnableWeb {
-		return s.handleStatic(w, r)
-	} else if r.Method == http.MethodGet && docsRegex.MatchString(r.URL.Path) && s.config.EnableWeb {
-		return s.handleDocs(w, r)
+	} else if r.Method == http.MethodGet && staticRegex.MatchString(r.URL.Path) {
+		return s.ensureWebEnabled(s.handleStatic)(w, r, v)
+	} else if r.Method == http.MethodGet && docsRegex.MatchString(r.URL.Path) {
+		return s.ensureWebEnabled(s.handleDocs)(w, r, v)
 	} else if r.Method == http.MethodGet && fileRegex.MatchString(r.URL.Path) && s.config.AttachmentCacheDir != "" {
 		return s.limitRequests(s.handleFile)(w, r, v)
 	} 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 == "/" {
 		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) {
@@ -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) {
 		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)) {
-		return s.handleTopic(w, r)
+		return s.ensureWebEnabled(s.handleTopic)(w, r, v)
 	}
 	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 {
 		r.URL.Path = webAppIndex
 	} else {
 		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!
 	if unifiedpush {
 		w.Header().Set("Content-Type", "application/json")
@@ -321,7 +321,7 @@ func (s *Server) handleTopic(w http.ResponseWriter, r *http.Request) error {
 		return err
 	}
 	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 {
@@ -335,12 +335,12 @@ func (s *Server) handleTopicAuth(w http.ResponseWriter, _ *http.Request, _ *visi
 	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)
 	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 := "/"
 	if !s.config.WebRootIsApp {
 		appRoot = "/app"
@@ -368,13 +368,13 @@ func (s *Server) handleUserStats(w http.ResponseWriter, r *http.Request, v *visi
 	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
 	util.Gzip(http.FileServer(http.FS(webFsCached))).ServeHTTP(w, r)
 	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)
 	return nil
 }
@@ -905,7 +905,7 @@ func parseSince(r *http.Request, poll bool) (sinceMarker, error) {
 	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-Origin", "*")  // CORS, allow cross-origin requests
 	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
 // 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 {

+ 1 - 1
server/server.yml

@@ -128,7 +128,7 @@
 
 # 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.
-# 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