diff options
Diffstat (limited to 'internal/web/server.go')
| -rw-r--r-- | internal/web/server.go | 43 |
1 files changed, 27 insertions, 16 deletions
diff --git a/internal/web/server.go b/internal/web/server.go index e8a5e6d..68dba3b 100644 --- a/internal/web/server.go +++ b/internal/web/server.go @@ -29,33 +29,26 @@ type Server struct { func NewServer(cfg *config.Config, st *store.Store) *Server { s := &Server{cfg: cfg, st: st, tpl: newTemplates(cfg.Dev, funcs)} s.root = s.rootRoutes() - s.blog = s.blogRoutes() + s.blog = s.subdomainRoutes() return s } // ServeHTTP dispatches on the Host header: the base domain is the management -// site, one label below it is a blog, anything else is a 404. +// site plus the superadmin's root blog, one label below it is a blog, anything +// else is a 404. func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { host := hostname(r.Host) switch { - case host == s.cfg.BaseDomain, host == "www."+s.cfg.BaseDomain: - s.root.ServeHTTP(w, r) + case host == s.cfg.BaseDomain, host == config.RootSubdomain+"."+s.cfg.BaseDomain: + // The root blog is looked up lazily by hostBlog so management pages work even without one. + s.root.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), ctxHostSub, config.RootSubdomain))) case strings.HasSuffix(host, "."+s.cfg.BaseDomain): sub := strings.TrimSuffix(host, "."+s.cfg.BaseDomain) if strings.Contains(sub, ".") || reservedSubdomains[sub] { http.NotFound(w, r) return } - blog, err := s.st.BlogBySubdomain(r.Context(), sub) - if err != nil { - if errors.Is(err, store.ErrNotFound) { - s.plainError(w, http.StatusNotFound, "No blog here (yet).") - return - } - s.serverError(w, err) - return - } - s.blog.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), ctxBlog, blog))) + s.blog.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), ctxHostSub, sub))) default: s.plainError(w, http.StatusNotFound, fmt.Sprintf("Unknown host %q. Blogs live at <name>.%s", host, s.cfg.BaseDomain)) } @@ -73,8 +66,9 @@ func hostname(h string) string { type ctxKey int const ( - ctxUser ctxKey = iota - ctxBlog + ctxUser ctxKey = iota + ctxBlog // the blog a handler operates on (public page, or /b/{sub}/ management) + ctxHostSub // subdomain derived from the Host header ) func currentUser(r *http.Request) *store.User { @@ -89,6 +83,23 @@ func currentBlog(r *http.Request) *store.Blog { // ---- middleware ---------------------------------------------------------- +// hostBlog resolves the blog named by the Host header into the context for public pages. +func (s *Server) hostBlog(next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + sub, _ := r.Context().Value(ctxHostSub).(string) + blog, err := s.st.BlogBySubdomain(r.Context(), sub) + if err != nil { + if errors.Is(err, store.ErrNotFound) { + s.plainError(w, http.StatusNotFound, "No blog here (yet).") + return + } + s.serverError(w, err) + return + } + next(w, r.WithContext(context.WithValue(r.Context(), ctxBlog, blog))) + } +} + // session loads the user from the JWT cookie (if any) into the context. func (s *Server) session(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
