aboutsummaryrefslogtreecommitdiffstats
path: root/internal/web
diff options
context:
space:
mode:
Diffstat (limited to 'internal/web')
-rw-r--r--internal/web/handlers_auth.go17
-rw-r--r--internal/web/handlers_blog.go3
-rw-r--r--internal/web/handlers_pages.go2
-rw-r--r--internal/web/routes.go6
-rw-r--r--internal/web/server.go2
-rw-r--r--internal/web/templates/auth/login.html2
-rw-r--r--internal/web/templates/layouts/blog.html2
-rw-r--r--internal/web/web_test.go23
8 files changed, 41 insertions, 16 deletions
diff --git a/internal/web/handlers_auth.go b/internal/web/handlers_auth.go
index 5d82ead..7c1d1e8 100644
--- a/internal/web/handlers_auth.go
+++ b/internal/web/handlers_auth.go
@@ -11,11 +11,22 @@ import (
)
func (s *Server) handleLoginForm(w http.ResponseWriter, r *http.Request) {
+ next := safeNext(r.URL.Query().Get("next"))
if currentUser(r) != nil {
- http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
+ if next == "" {
+ next = "/dashboard"
+ }
+ http.Redirect(w, r, next, http.StatusSeeOther)
return
}
- s.render(w, r, "auth/login.html", map[string]any{"next": safeNext(r.URL.Query().Get("next"))})
+ s.render(w, r, "auth/login.html", map[string]any{"next": next})
+}
+
+// handleWebadminRedirect serves /webadmin on a blog's own host: the login page
+// lives on the root domain, so bounce there and come back to this blog's dashboard.
+func (s *Server) handleWebadminRedirect(w http.ResponseWriter, r *http.Request) {
+ sub, _ := r.Context().Value(ctxHostSub).(string)
+ http.Redirect(w, r, s.cfg.RootURL()+"/webadmin?next="+urlQuery("/b/"+sub+"/"), http.StatusSeeOther)
}
func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
@@ -54,7 +65,7 @@ func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
func (s *Server) handleLogout(w http.ResponseWriter, r *http.Request) {
auth.ClearSessionCookie(w)
- http.Redirect(w, r, "/login", http.StatusSeeOther)
+ http.Redirect(w, r, "/webadmin", http.StatusSeeOther)
}
func (s *Server) handleDashboard(w http.ResponseWriter, r *http.Request) {
diff --git a/internal/web/handlers_blog.go b/internal/web/handlers_blog.go
index 27e67e6..b768c4d 100644
--- a/internal/web/handlers_blog.go
+++ b/internal/web/handlers_blog.go
@@ -7,7 +7,6 @@ import (
"strconv"
"time"
- "github.com/gramanas/blogspace/internal/config"
"github.com/gramanas/blogspace/internal/store"
)
@@ -27,7 +26,7 @@ func (s *Server) blogView(r *http.Request) (map[string]any, error) {
nav = append(nav, p)
}
}
- return map[string]any{"theme": theme, "css": theme.CSS(), "nav": nav, "isRoot": blog.Subdomain == config.RootSubdomain}, nil
+ return map[string]any{"theme": theme, "css": theme.CSS(), "nav": nav}, nil
}
func (s *Server) handleBlogHome(w http.ResponseWriter, r *http.Request) {
diff --git a/internal/web/handlers_pages.go b/internal/web/handlers_pages.go
index ac50513..db46097 100644
--- a/internal/web/handlers_pages.go
+++ b/internal/web/handlers_pages.go
@@ -13,7 +13,7 @@ import (
// Page slugs that would collide with blog routes, or with management routes on the root domain.
var reservedPageSlugs = map[string]bool{"media": true, "static": true, "feed.xml": true,
- "login": true, "logout": true, "dashboard": true, "account": true, "admin": true, "b": true, "healthz": true}
+ "webadmin": true, "logout": true, "dashboard": true, "account": true, "admin": true, "b": true, "healthz": true}
func (s *Server) handlePages(w http.ResponseWriter, r *http.Request) {
pages, err := s.st.ListPages(r.Context(), currentBlog(r).ID)
diff --git a/internal/web/routes.go b/internal/web/routes.go
index 02d536a..e135229 100644
--- a/internal/web/routes.go
+++ b/internal/web/routes.go
@@ -10,8 +10,8 @@ func urlQuery(s string) string { return url.QueryEscape(s) }
func (s *Server) rootRoutes() http.Handler {
m := http.NewServeMux()
m.HandleFunc("GET /healthz", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("ok")) })
- m.HandleFunc("GET /login", s.handleLoginForm)
- m.HandleFunc("POST /login", s.handleLogin)
+ m.HandleFunc("GET /webadmin", s.handleLoginForm)
+ m.HandleFunc("POST /webadmin", s.handleLogin)
m.HandleFunc("POST /logout", s.handleLogout)
m.HandleFunc("GET /dashboard", s.requireAuth(s.handleDashboard))
m.HandleFunc("GET /account/password", s.requireAuth(s.handlePasswordForm))
@@ -71,6 +71,8 @@ func (s *Server) blogRoutes(m *http.ServeMux, wrap func(http.HandlerFunc) http.H
func (s *Server) subdomainRoutes() http.Handler {
m := http.NewServeMux()
+ // Bloggers type /webadmin on their own blog; send them to the real login page.
+ m.HandleFunc("GET /webadmin", s.handleWebadminRedirect)
m.HandleFunc("GET /media/{id}", s.handleMedia)
m.Handle("GET /static/{file}", s.staticHandler())
s.blogRoutes(m, s.hostBlog)
diff --git a/internal/web/server.go b/internal/web/server.go
index 68dba3b..cbeac5d 100644
--- a/internal/web/server.go
+++ b/internal/web/server.go
@@ -129,7 +129,7 @@ func (s *Server) requireAuth(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
u := currentUser(r)
if u == nil {
- http.Redirect(w, r, "/login?next="+r.URL.Path, http.StatusSeeOther)
+ http.Redirect(w, r, "/webadmin?next="+r.URL.Path, http.StatusSeeOther)
return
}
if r.Method == http.MethodPost {
diff --git a/internal/web/templates/auth/login.html b/internal/web/templates/auth/login.html
index 2229849..2aec3b1 100644
--- a/internal/web/templates/auth/login.html
+++ b/internal/web/templates/auth/login.html
@@ -2,7 +2,7 @@
{{define "content"}}
<div class="card narrow">
<h1>Log in</h1>
- <form method="post" action="/login">
+ <form method="post" action="/webadmin">
<input type="hidden" name="next" value="{{.Data.next}}">
<label>Username<br><input name="username" value="{{.Data.username}}" required autofocus autocomplete="username"></label>
<label>Password<br><input type="password" name="password" required autocomplete="current-password"></label>
diff --git a/internal/web/templates/layouts/blog.html b/internal/web/templates/layouts/blog.html
index ff66c59..7e3128e 100644
--- a/internal/web/templates/layouts/blog.html
+++ b/internal/web/templates/layouts/blog.html
@@ -27,7 +27,7 @@
<div class="site-footer">
<div class="wrap footer-inner">
{{if .Data.theme.FooterText}}<p>{{.Data.theme.FooterText}}</p>{{end}}
- <p class="small"><a href="/feed.xml">RSS</a> &middot; {{.Blog.Title}}{{if .Data.isRoot}} &middot; <a href="/login">Log in</a>{{end}}</p>
+ <p class="small"><a href="/feed.xml">RSS</a> &middot; {{.Blog.Title}}</p>
</div>
</div>
</body>
diff --git a/internal/web/web_test.go b/internal/web/web_test.go
index dcd766c..6e53bae 100644
--- a/internal/web/web_test.go
+++ b/internal/web/web_test.go
@@ -32,14 +32,14 @@ func TestHostRoutingWithoutDB(t *testing.T) {
t.Errorf("host %q: got %d, want 404", host, rec.Code)
}
}
- // root domain (and www) reach the management mux: /login renders without DB access
+ // root domain (and www) reach the management mux: /webadmin renders without DB access
for _, host := range []string{"example.com", "www.example.com", "example.com:8080"} {
rec := httptest.NewRecorder()
- req := httptest.NewRequest("GET", "/login", nil)
+ req := httptest.NewRequest("GET", "/webadmin", nil)
req.Host = host
s.ServeHTTP(rec, req)
if rec.Code != http.StatusOK || !strings.Contains(rec.Body.String(), "Log in") {
- t.Errorf("host %q /login: got %d", host, rec.Code)
+ t.Errorf("host %q /webadmin: got %d", host, rec.Code)
}
}
}
@@ -48,7 +48,7 @@ func TestHostRoutingWithoutDB(t *testing.T) {
func TestRootRoutePrecedence(t *testing.T) {
cfg := &config.Config{BaseDomain: "example.com", JWTSecret: []byte("x"), MaxUploadBytes: 1 << 20}
s := NewServer(cfg, nil)
- for path, want := range map[string]int{"/login": 200, "/healthz": 200, "/dashboard": 303, "/admin/": 303, "/b/alice/posts": 303, "/account/password": 303} {
+ for path, want := range map[string]int{"/webadmin": 200, "/healthz": 200, "/dashboard": 303, "/admin/": 303, "/b/alice/posts": 303, "/account/password": 303} {
rec := httptest.NewRecorder()
req := httptest.NewRequest("GET", path, nil)
req.Host = "example.com"
@@ -57,7 +57,7 @@ func TestRootRoutePrecedence(t *testing.T) {
t.Errorf("%s: got %d, want %d", path, rec.Code, want)
}
}
- for _, slug := range []string{"login", "admin", "b", "media", "feed.xml"} {
+ for _, slug := range []string{"webadmin", "admin", "b", "media", "feed.xml"} {
if !reservedPageSlugs[slug] {
t.Errorf("page slug %q should be reserved", slug)
}
@@ -91,3 +91,16 @@ func TestAllTemplatesParse(t *testing.T) {
}
}
}
+
+// /webadmin on a blog's own host bounces to the root login page and back to that blog's dashboard.
+func TestSubdomainWebadminRedirect(t *testing.T) {
+ cfg := &config.Config{BaseDomain: "example.com", JWTSecret: []byte("x"), MaxUploadBytes: 1 << 20}
+ s := NewServer(cfg, nil)
+ rec := httptest.NewRecorder()
+ req := httptest.NewRequest("GET", "/webadmin", nil)
+ req.Host = "alice.example.com"
+ s.ServeHTTP(rec, req)
+ if want := "http://example.com/webadmin?next=%2Fb%2Falice%2F"; rec.Code != http.StatusSeeOther || rec.Header().Get("Location") != want {
+ t.Errorf("got %d %q, want 303 %q", rec.Code, rec.Header().Get("Location"), want)
+ }
+}