Browse Source

Merge pull request #249 from Curid/main

Add disable option to web-root
Philipp C. Heckel 2 years ago
parent
commit
797e4640df
7 changed files with 65 additions and 21 deletions
  1. 1 0
      .gitignore
  2. 9 5
      cmd/serve_linux.go
  3. 1 1
      docs/config.md
  4. 2 0
      server/config.go
  5. 11 10
      server/server.go
  6. 2 1
      server/server.yml
  7. 39 4
      server/server_test.go

+ 1 - 0
.gitignore

@@ -1,6 +1,7 @@
 dist/
 build/
 .idea/
+*.swp
 server/docs/
 server/site/
 tools/fbsend/fbsend

+ 9 - 5
cmd/serve_linux.go

@@ -3,15 +3,16 @@ package cmd
 import (
 	"errors"
 	"fmt"
-	"github.com/urfave/cli/v2"
-	"github.com/urfave/cli/v2/altsrc"
-	"heckel.io/ntfy/server"
-	"heckel.io/ntfy/util"
 	"log"
 	"math"
 	"net"
 	"strings"
 	"time"
+
+	"github.com/urfave/cli/v2"
+	"github.com/urfave/cli/v2/altsrc"
+	"heckel.io/ntfy/server"
+	"heckel.io/ntfy/util"
 )
 
 func init() {
@@ -146,8 +147,10 @@ func execServe(c *cli.Context) error {
 		return errors.New("if set, web-root must be 'home' or 'app'")
 	}
 
-	// Default auth permissions
 	webRootIsApp := webRoot == "app"
+	enableWeb := webRoot != "disable"
+
+	// Default auth permissions
 	authDefaultRead := authDefaultAccess == "read-write" || authDefaultAccess == "read-only"
 	authDefaultWrite := authDefaultAccess == "read-write" || authDefaultAccess == "write-only"
 
@@ -227,6 +230,7 @@ func execServe(c *cli.Context) error {
 	conf.VisitorEmailLimitBurst = visitorEmailLimitBurst
 	conf.VisitorEmailLimitReplenish = visitorEmailLimitReplenish
 	conf.BehindProxy = behindProxy
+	conf.EnableWeb = enableWeb
 	s, err := server.New(conf)
 	if err != nil {
 		log.Fatalln(err)

+ 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) or web app (app)                                                                                                                                                                           |
+| `web-root`                                 | `NTFY_WEB_ROOT`                                 | `app` or `home`                                     | `app`        | Sets web root to landing page (home), web app (app) or (disable) for no WebUI.                                                                                                                                                  |
 | `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`.                                                 |

+ 2 - 0
server/config.go

@@ -88,6 +88,7 @@ type Config struct {
 	VisitorEmailLimitBurst               int
 	VisitorEmailLimitReplenish           time.Duration
 	BehindProxy                          bool
+	EnableWeb                            bool
 }
 
 // NewConfig instantiates a default new server config
@@ -126,5 +127,6 @@ func NewConfig() *Config {
 		VisitorEmailLimitBurst:               DefaultVisitorEmailLimitBurst,
 		VisitorEmailLimitReplenish:           DefaultVisitorEmailLimitReplenish,
 		BehindProxy:                          false,
+		EnableWeb:                            true,
 	}
 }

+ 11 - 10
server/server.go

@@ -8,11 +8,6 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"github.com/emersion/go-smtp"
-	"github.com/gorilla/websocket"
-	"golang.org/x/sync/errgroup"
-	"heckel.io/ntfy/auth"
-	"heckel.io/ntfy/util"
 	"io"
 	"log"
 	"net"
@@ -28,6 +23,12 @@ import (
 	"sync"
 	"time"
 	"unicode/utf8"
+
+	"github.com/emersion/go-smtp"
+	"github.com/gorilla/websocket"
+	"golang.org/x/sync/errgroup"
+	"heckel.io/ntfy/auth"
+	"heckel.io/ntfy/util"
 )
 
 // Server is the main server, providing the UI and API for ntfy
@@ -262,19 +263,19 @@ 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 == "/" {
+	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" {
+	} else if r.Method == http.MethodGet && r.URL.Path == "/example.html" && s.config.EnableWeb {
 		return s.handleExample(w, r)
 	} 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 {
+	} else if r.Method == http.MethodGet && r.URL.Path == webConfigPath && s.config.EnableWeb {
 		return s.handleWebConfig(w, r)
 	} 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) {
+	} 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) {
+	} 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 && fileRegex.MatchString(r.URL.Path) && s.config.AttachmentCacheDir != "" {
 		return s.limitRequests(s.handleFile)(w, r, v)

+ 2 - 1
server/server.yml

@@ -127,7 +127,8 @@
 # manager-interval: "1m"
 
 # 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) or "home".
+# web app. If you self-host, you don't want to change this.
+# Can be "app" (default), "home" or "disable" to disable the WebUI.
 #
 # web-root: app
 

+ 39 - 4
server/server_test.go

@@ -6,9 +6,6 @@ import (
 	"encoding/base64"
 	"encoding/json"
 	"fmt"
-	"github.com/stretchr/testify/require"
-	"heckel.io/ntfy/auth"
-	"heckel.io/ntfy/util"
 	"math/rand"
 	"net/http"
 	"net/http/httptest"
@@ -18,6 +15,10 @@ import (
 	"sync"
 	"testing"
 	"time"
+
+	"github.com/stretchr/testify/require"
+	"heckel.io/ntfy/auth"
+	"heckel.io/ntfy/util"
 )
 
 func TestServer_PublishAndPoll(t *testing.T) {
@@ -162,6 +163,40 @@ func TestServer_StaticSites(t *testing.T) {
 	require.Contains(t, rr.Body.String(), "</html>")
 }
 
+func TestServer_WebEnabled(t *testing.T) {
+	conf := newTestConfig(t)
+	conf.EnableWeb = false
+	s := newTestServer(t, conf)
+
+	rr := request(t, s, "GET", "/", "", nil)
+	require.Equal(t, 404, rr.Code)
+
+	rr = request(t, s, "GET", "/example.html", "", nil)
+	require.Equal(t, 404, rr.Code)
+
+	rr = request(t, s, "GET", "/config.js", "", nil)
+	require.Equal(t, 404, rr.Code)
+
+	rr = request(t, s, "GET", "/static/css/home.css", "", nil)
+	require.Equal(t, 404, rr.Code)
+
+	conf2 := newTestConfig(t)
+	conf2.EnableWeb = true
+	s2 := newTestServer(t, conf2)
+
+	rr = request(t, s2, "GET", "/", "", nil)
+	require.Equal(t, 200, rr.Code)
+
+	rr = request(t, s2, "GET", "/example.html", "", nil)
+	require.Equal(t, 200, rr.Code)
+
+	rr = request(t, s2, "GET", "/config.js", "", nil)
+	require.Equal(t, 200, rr.Code)
+
+	rr = request(t, s2, "GET", "/static/css/home.css", "", nil)
+	require.Equal(t, 200, rr.Code)
+}
+
 func TestServer_PublishLargeMessage(t *testing.T) {
 	c := newTestConfig(t)
 	c.AttachmentCacheDir = "" // Disable attachments
@@ -1303,7 +1338,7 @@ func firebaseServiceAccountFile(t *testing.T) string {
 		return os.Getenv("NTFY_TEST_FIREBASE_SERVICE_ACCOUNT_FILE")
 	} else if os.Getenv("NTFY_TEST_FIREBASE_SERVICE_ACCOUNT") != "" {
 		filename := filepath.Join(t.TempDir(), "firebase.json")
-		require.NotNil(t, os.WriteFile(filename, []byte(os.Getenv("NTFY_TEST_FIREBASE_SERVICE_ACCOUNT")), 0600))
+		require.NotNil(t, os.WriteFile(filename, []byte(os.Getenv("NTFY_TEST_FIREBASE_SERVICE_ACCOUNT")), 0o600))
 		return filename
 	}
 	t.SkipNow()