diff options
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/web/handlers_auth.go | 17 | ||||
| -rw-r--r-- | internal/web/handlers_blog.go | 3 | ||||
| -rw-r--r-- | internal/web/handlers_pages.go | 2 | ||||
| -rw-r--r-- | internal/web/routes.go | 6 | ||||
| -rw-r--r-- | internal/web/server.go | 2 | ||||
| -rw-r--r-- | internal/web/templates/auth/login.html | 2 | ||||
| -rw-r--r-- | internal/web/templates/layouts/blog.html | 2 | ||||
| -rw-r--r-- | internal/web/web_test.go | 23 |
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> · {{.Blog.Title}}{{if .Data.isRoot}} · <a href="/login">Log in</a>{{end}}</p> + <p class="small"><a href="/feed.xml">RSS</a> · {{.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) + } +} |
