Преглед на файлове

Add support for base URLs with subfolders

Frédéric Guillot преди 8 години
родител
ревизия
9c42997209
променени са 10 файла, в които са добавени 105 реда и са изтрити 23 реда
  1. 31 6
      config/config.go
  2. 46 3
      config/config_test.go
  3. 4 0
      daemon/routes.go
  4. 11 4
      http/cookie/cookie.go
  5. 1 1
      http/middleware/session.go
  6. 2 2
      template/html/integrations.html
  7. 3 0
      template/template.go
  8. 4 4
      template/views.go
  9. 2 2
      ui/login.go
  10. 1 1
      ui/oauth2.go

+ 31 - 6
config/config.go

@@ -5,6 +5,7 @@
 package config
 
 import (
+	"net/url"
 	"os"
 	"strconv"
 )
@@ -26,7 +27,10 @@ const (
 
 // Config manages configuration parameters.
 type Config struct {
-	IsHTTPS bool
+	IsHTTPS  bool
+	baseURL  string
+	rootURL  string
+	basePath string
 }
 
 func (c *Config) get(key, fallback string) string {
@@ -53,13 +57,34 @@ func (c *Config) HasDebugMode() bool {
 	return c.get("DEBUG", "") != ""
 }
 
-// BaseURL returns the application base URL.
+// BaseURL returns the application base URL with path.
 func (c *Config) BaseURL() string {
-	baseURL := c.get("BASE_URL", defaultBaseURL)
-	if baseURL[len(baseURL)-1:] == "/" {
-		baseURL = baseURL[:len(baseURL)-1]
+	if c.baseURL == "" {
+		c.baseURL = c.get("BASE_URL", defaultBaseURL)
+		if c.baseURL[len(c.baseURL)-1:] == "/" {
+			c.baseURL = c.baseURL[:len(c.baseURL)-1]
+		}
 	}
-	return baseURL
+	return c.baseURL
+}
+
+// RootURL returns the base URL without path.
+func (c *Config) RootURL() string {
+	if c.rootURL == "" {
+		u, _ := url.Parse(c.BaseURL())
+		u.Path = ""
+		c.rootURL = u.String()
+	}
+	return c.rootURL
+}
+
+// BasePath returns the application base path according to the base URL.
+func (c *Config) BasePath() string {
+	if c.basePath == "" {
+		u, _ := url.Parse(c.BaseURL())
+		c.basePath = u.Path
+	}
+	return c.basePath
 }
 
 // DatabaseURL returns the database URL.

+ 46 - 3
config/config_test.go

@@ -9,7 +9,26 @@ import (
 	"testing"
 )
 
-func TestGetCustomBaseURL(t *testing.T) {
+func TestDebugModeOn(t *testing.T) {
+	os.Clearenv()
+	os.Setenv("DEBUG", "1")
+	cfg := NewConfig()
+
+	if !cfg.HasDebugMode() {
+		t.Fatalf(`Unexpected debug mode value, got "%v"`, cfg.HasDebugMode())
+	}
+}
+
+func TestDebugModeOff(t *testing.T) {
+	os.Clearenv()
+	cfg := NewConfig()
+
+	if cfg.HasDebugMode() {
+		t.Fatalf(`Unexpected debug mode value, got "%v"`, cfg.HasDebugMode())
+	}
+}
+
+func TestCustomBaseURL(t *testing.T) {
 	os.Clearenv()
 	os.Setenv("BASE_URL", "http://example.org")
 	cfg := NewConfig()
@@ -17,9 +36,17 @@ func TestGetCustomBaseURL(t *testing.T) {
 	if cfg.BaseURL() != "http://example.org" {
 		t.Fatalf(`Unexpected base URL, got "%s"`, cfg.BaseURL())
 	}
+
+	if cfg.RootURL() != "http://example.org" {
+		t.Fatalf(`Unexpected root URL, got "%s"`, cfg.RootURL())
+	}
+
+	if cfg.BasePath() != "" {
+		t.Fatalf(`Unexpected base path, got "%s"`, cfg.BasePath())
+	}
 }
 
-func TestGetCustomBaseURLWithTrailingSlash(t *testing.T) {
+func TestCustomBaseURLWithTrailingSlash(t *testing.T) {
 	os.Clearenv()
 	os.Setenv("BASE_URL", "http://example.org/folder/")
 	cfg := NewConfig()
@@ -27,13 +54,29 @@ func TestGetCustomBaseURLWithTrailingSlash(t *testing.T) {
 	if cfg.BaseURL() != "http://example.org/folder" {
 		t.Fatalf(`Unexpected base URL, got "%s"`, cfg.BaseURL())
 	}
+
+	if cfg.RootURL() != "http://example.org" {
+		t.Fatalf(`Unexpected root URL, got "%s"`, cfg.BaseURL())
+	}
+
+	if cfg.BasePath() != "/folder" {
+		t.Fatalf(`Unexpected base path, got "%s"`, cfg.BasePath())
+	}
 }
 
-func TestGetDefaultBaseURL(t *testing.T) {
+func TestDefaultBaseURL(t *testing.T) {
 	os.Clearenv()
 	cfg := NewConfig()
 
 	if cfg.BaseURL() != "http://localhost" {
 		t.Fatalf(`Unexpected base URL, got "%s"`, cfg.BaseURL())
 	}
+
+	if cfg.RootURL() != "http://localhost" {
+		t.Fatalf(`Unexpected root URL, got "%s"`, cfg.RootURL())
+	}
+
+	if cfg.BasePath() != "" {
+		t.Fatalf(`Unexpected base path, got "%s"`, cfg.BasePath())
+	}
 }

+ 4 - 0
daemon/routes.go

@@ -45,6 +45,10 @@ func routes(cfg *config.Config, store *storage.Storage, feedHandler *feed.Handle
 		middleware.NewSessionMiddleware(cfg, store).Handler,
 	))
 
+	if cfg.BasePath() != "" {
+		router = router.PathPrefix(cfg.BasePath()).Subrouter()
+	}
+
 	router.Handle("/fever/", feverHandler.Use(feverController.Handler)).Name("feverEndpoint")
 
 	router.Handle("/v1/users", apiHandler.Use(apiController.CreateUser)).Methods("POST")

+ 11 - 4
http/cookie/cookie.go

@@ -19,11 +19,11 @@ const (
 )
 
 // New creates a new cookie.
-func New(name, value string, isHTTPS bool) *http.Cookie {
+func New(name, value string, isHTTPS bool, path string) *http.Cookie {
 	return &http.Cookie{
 		Name:     name,
 		Value:    value,
-		Path:     "/",
+		Path:     basePath(path),
 		Secure:   isHTTPS,
 		HttpOnly: true,
 		Expires:  time.Now().Add(cookieDuration * 24 * time.Hour),
@@ -31,14 +31,21 @@ func New(name, value string, isHTTPS bool) *http.Cookie {
 }
 
 // Expired returns an expired cookie.
-func Expired(name string, isHTTPS bool) *http.Cookie {
+func Expired(name string, isHTTPS bool, path string) *http.Cookie {
 	return &http.Cookie{
 		Name:     name,
 		Value:    "",
-		Path:     "/",
+		Path:     basePath(path),
 		Secure:   isHTTPS,
 		HttpOnly: true,
 		MaxAge:   -1,
 		Expires:  time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC),
 	}
 }
+
+func basePath(path string) string {
+	if path == "" {
+		return "/"
+	}
+	return path
+}

+ 1 - 1
http/middleware/session.go

@@ -36,7 +36,7 @@ func (s *SessionMiddleware) Handler(next http.Handler) http.Handler {
 				return
 			}
 
-			http.SetCookie(w, cookie.New(cookie.CookieSessionID, session.ID, s.cfg.IsHTTPS))
+			http.SetCookie(w, cookie.New(cookie.CookieSessionID, session.ID, s.cfg.IsHTTPS, s.cfg.BasePath()))
 		} else {
 			logger.Debug("[Middleware:Session] %s", session)
 		}

+ 2 - 2
template/html/integrations.html

@@ -40,7 +40,7 @@
         <label for="form-fever-password">{{ t "Fever Password" }}</label>
         <input type="password" name="fever_password" id="form-fever-password" value="{{ .form.FeverPassword }}">
 
-        <p>{{ t "Fever API endpoint:" }} <strong>{{ baseURL }}{{ route "feverEndpoint" }}</strong></p>
+        <p>{{ t "Fever API endpoint:" }} <strong>{{ rootURL }}{{ route "feverEndpoint" }}</strong></p>
     </div>
 
     <h3>Pinboard</h3>
@@ -120,7 +120,7 @@
     <p>{{ t "This special link allows you to subscribe to a website directly by using a bookmark in your web browser." }}</p>
 
     <div class="bookmarklet">
-        <a href="javascript:location.href='{{ baseURL }}{{ route "bookmarklet" }}?uri='+encodeURIComponent(window.location.href)">{{ t "Add to Miniflux" }}</a>
+        <a href="javascript:location.href='{{ rootURL }}{{ route "bookmarklet" }}?uri='+encodeURIComponent(window.location.href)">{{ t "Add to Miniflux" }}</a>
     </div>
 
     <p>{{ t "Drag and drop this link to your bookmarks." }}</p>

+ 3 - 0
template/template.go

@@ -38,6 +38,9 @@ func (e *Engine) parseAll() {
 		"baseURL": func() string {
 			return e.cfg.BaseURL()
 		},
+		"rootURL": func() string {
+			return e.cfg.RootURL()
+		},
 		"hasOAuth2Provider": func(provider string) bool {
 			return e.cfg.OAuth2Provider() == provider
 		},

+ 4 - 4
template/views.go

@@ -1,5 +1,5 @@
 // Code generated by go generate; DO NOT EDIT.
-// 2018-01-31 22:01:40.010173412 -0800 PST m=+0.035694895
+// 2018-02-03 15:28:45.540437885 -0800 PST m=+0.032377624
 
 package template
 
@@ -815,7 +815,7 @@ var templateViewsMap = map[string]string{
         <label for="form-fever-password">{{ t "Fever Password" }}</label>
         <input type="password" name="fever_password" id="form-fever-password" value="{{ .form.FeverPassword }}">
 
-        <p>{{ t "Fever API endpoint:" }} <strong>{{ baseURL }}{{ route "feverEndpoint" }}</strong></p>
+        <p>{{ t "Fever API endpoint:" }} <strong>{{ rootURL }}{{ route "feverEndpoint" }}</strong></p>
     </div>
 
     <h3>Pinboard</h3>
@@ -895,7 +895,7 @@ var templateViewsMap = map[string]string{
     <p>{{ t "This special link allows you to subscribe to a website directly by using a bookmark in your web browser." }}</p>
 
     <div class="bookmarklet">
-        <a href="javascript:location.href='{{ baseURL }}{{ route "bookmarklet" }}?uri='+encodeURIComponent(window.location.href)">{{ t "Add to Miniflux" }}</a>
+        <a href="javascript:location.href='{{ rootURL }}{{ route "bookmarklet" }}?uri='+encodeURIComponent(window.location.href)">{{ t "Add to Miniflux" }}</a>
     </div>
 
     <p>{{ t "Drag and drop this link to your bookmarks." }}</p>
@@ -1225,7 +1225,7 @@ var templateViewsMapChecksums = map[string]string{
 	"feeds":               "65b0a47c4438810b9d51c60f3f3b2519690e56ff74029e6296c68626b83a470b",
 	"history":             "d2476fd727e4f53428b5ed1f3f9423063583337ec8cfe1dd9c931fcb03852a20",
 	"import":              "73b5112e20bfd232bf73334544186ea419505936bc237d481517a8622901878f",
-	"integrations":        "a677434e9a8be1f80cfbc1d04828dacc7abcec2a22b45c80594d49cc2ba7c0e5",
+	"integrations":        "958b73d632a3e2a79368bb1582efb8aabc438cef4fa6e8dc1aa4932494916aca",
 	"login":               "7d83c3067c02f1f6aafdd8816c7f97a4eb5a5a4bdaaaa4cc1e2fbb9c17ea65e8",
 	"sessions":            "d8ef5900d8ea8395804b320002e5f45ed0ab8b790e43f674f61f8b9787041cbd",
 	"settings":            "ea2505b9d0a6d6bb594dba87a92079de19baa6d494f0651693a7685489fb7de9",

+ 2 - 2
ui/login.go

@@ -59,7 +59,7 @@ func (c *Controller) CheckLogin(ctx *handler.Context, request *handler.Request,
 
 	logger.Info("[Controller:CheckLogin] username=%s just logged in", authForm.Username)
 
-	response.SetCookie(cookie.New(cookie.CookieUserSessionID, sessionToken, c.cfg.IsHTTPS))
+	response.SetCookie(cookie.New(cookie.CookieUserSessionID, sessionToken, c.cfg.IsHTTPS, c.cfg.BasePath()))
 	response.Redirect(ctx.Route("unread"))
 }
 
@@ -75,6 +75,6 @@ func (c *Controller) Logout(ctx *handler.Context, request *handler.Request, resp
 		logger.Error("[Controller:Logout] %v", err)
 	}
 
-	response.SetCookie(cookie.Expired(cookie.CookieUserSessionID, c.cfg.IsHTTPS))
+	response.SetCookie(cookie.Expired(cookie.CookieUserSessionID, c.cfg.IsHTTPS, c.cfg.BasePath()))
 	response.Redirect(ctx.Route("login"))
 }

+ 1 - 1
ui/oauth2.go

@@ -132,7 +132,7 @@ func (c *Controller) OAuth2Callback(ctx *handler.Context, request *handler.Reque
 
 	logger.Info("[Controller:OAuth2Callback] username=%s just logged in", user.Username)
 
-	response.SetCookie(cookie.New(cookie.CookieUserSessionID, sessionToken, c.cfg.IsHTTPS))
+	response.SetCookie(cookie.New(cookie.CookieUserSessionID, sessionToken, c.cfg.IsHTTPS, c.cfg.BasePath()))
 	response.Redirect(ctx.Route("unread"))
 }