aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Frédéric Guillot <fred@miniflux.net>2018-02-03 15:33:17 -0800
committerGravatar Frédéric Guillot <fred@miniflux.net>2018-02-03 15:33:17 -0800
commit9c4299720900fce52daedfce2314d31e92f7fe1d (patch)
tree2a67a75d71011d071910a4cb4216d45ee9d904c4
parent78385a351e208feb183549c2fa854b302da0c690 (diff)
Add support for base URLs with subfolders
-rw-r--r--config/config.go37
-rw-r--r--config/config_test.go49
-rw-r--r--daemon/routes.go4
-rw-r--r--http/cookie/cookie.go15
-rw-r--r--http/middleware/session.go2
-rw-r--r--template/html/integrations.html4
-rw-r--r--template/template.go3
-rw-r--r--template/views.go8
-rw-r--r--ui/login.go4
-rw-r--r--ui/oauth2.go2
10 files changed, 105 insertions, 23 deletions
diff --git a/config/config.go b/config/config.go
index 7788a38..05b5d3b 100644
--- a/config/config.go
+++ b/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.
diff --git a/config/config_test.go b/config/config_test.go
index 4c01bc5..fbc7175 100644
--- a/config/config_test.go
+++ b/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())
+ }
}
diff --git a/daemon/routes.go b/daemon/routes.go
index 6b44fb4..35cec9e 100644
--- a/daemon/routes.go
+++ b/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")
diff --git a/http/cookie/cookie.go b/http/cookie/cookie.go
index d1f3e72..4407daa 100644
--- a/http/cookie/cookie.go
+++ b/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
+}
diff --git a/http/middleware/session.go b/http/middleware/session.go
index c3876f6..9b7dd86 100644
--- a/http/middleware/session.go
+++ b/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)
}
diff --git a/template/html/integrations.html b/template/html/integrations.html
index 8c597ee..91f5b7a 100644
--- a/template/html/integrations.html
+++ b/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>
diff --git a/template/template.go b/template/template.go
index e392dad..b3374ff 100644
--- a/template/template.go
+++ b/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
},
diff --git a/template/views.go b/template/views.go
index acf96d8..e189e5d 100644
--- a/template/views.go
+++ b/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",
diff --git a/ui/login.go b/ui/login.go
index f44e181..fc10bc3 100644
--- a/ui/login.go
+++ b/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"))
}
diff --git a/ui/oauth2.go b/ui/oauth2.go
index 4afc7d8..c5bf931 100644
--- a/ui/oauth2.go
+++ b/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"))
}