Add support for base URLs with subfolders
This commit is contained in:
parent
78385a351e
commit
9c42997209
10 changed files with 105 additions and 23 deletions
|
@ -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.
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
},
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"))
|
||||
}
|
||||
|
|
|
@ -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"))
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue