package web import ( "net/http" "net/url" ) 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 /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)) m.HandleFunc("POST /account/password", s.requireAuth(s.handlePassword)) // blog management (owner or superadmin) m.HandleFunc("GET /b/{sub}/{$}", s.withBlog(s.handleBlogOverview)) m.HandleFunc("GET /b/{sub}/settings", s.withBlog(s.handleSettingsForm)) m.HandleFunc("POST /b/{sub}/settings", s.withBlog(s.handleSettings)) m.HandleFunc("GET /b/{sub}/pages", s.withBlog(s.handlePages)) m.HandleFunc("GET /b/{sub}/pages/new", s.withBlog(s.handlePageForm)) m.HandleFunc("POST /b/{sub}/pages/new", s.withBlog(s.handlePageSave)) m.HandleFunc("GET /b/{sub}/pages/{id}/edit", s.withBlog(s.handlePageForm)) m.HandleFunc("POST /b/{sub}/pages/{id}/edit", s.withBlog(s.handlePageSave)) m.HandleFunc("GET /b/{sub}/pages/{id}/delete", s.withBlog(s.handlePageDeleteConfirm)) m.HandleFunc("POST /b/{sub}/pages/{id}/delete", s.withBlog(s.handlePageDelete)) m.HandleFunc("POST /b/{sub}/pages/{id}/move", s.withBlog(s.handlePageMove)) m.HandleFunc("POST /b/{sub}/pages/{id}/home", s.withBlog(s.handlePageHome)) m.HandleFunc("GET /b/{sub}/posts", s.withBlog(s.handlePosts)) m.HandleFunc("GET /b/{sub}/posts/new", s.withBlog(s.handlePostForm)) m.HandleFunc("POST /b/{sub}/posts/new", s.withBlog(s.handlePostSave)) m.HandleFunc("GET /b/{sub}/posts/{id}/edit", s.withBlog(s.handlePostForm)) m.HandleFunc("POST /b/{sub}/posts/{id}/edit", s.withBlog(s.handlePostSave)) m.HandleFunc("GET /b/{sub}/posts/{id}/delete", s.withBlog(s.handlePostDeleteConfirm)) m.HandleFunc("POST /b/{sub}/posts/{id}/delete", s.withBlog(s.handlePostDelete)) m.HandleFunc("GET /b/{sub}/design", s.withBlog(s.handleDesignForm)) m.HandleFunc("POST /b/{sub}/design", s.withBlog(s.handleDesign)) m.HandleFunc("GET /b/{sub}/images", s.withBlog(s.handleImages)) m.HandleFunc("POST /b/{sub}/images/upload", s.withBlog(s.handleImageUpload)) m.HandleFunc("POST /b/{sub}/images/{id}/delete", s.withBlog(s.handleImageDelete)) // superadmin m.HandleFunc("GET /admin/{$}", s.requireAdmin(s.handleAdmin)) m.HandleFunc("GET /admin/users/new", s.requireAdmin(s.handleAdminNewUserForm)) m.HandleFunc("POST /admin/users/new", s.requireAdmin(s.handleAdminNewUser)) m.HandleFunc("POST /admin/users/{id}/reset-password", s.requireAdmin(s.handleAdminResetPassword)) m.HandleFunc("POST /admin/users/{id}/disable", s.requireAdmin(s.handleAdminSetDisabled(true))) m.HandleFunc("POST /admin/users/{id}/enable", s.requireAdmin(s.handleAdminSetDisabled(false))) m.HandleFunc("GET /admin/users/{id}/delete", s.requireAdmin(s.handleAdminDeleteUserConfirm)) m.HandleFunc("POST /admin/users/{id}/delete", s.requireAdmin(s.handleAdminDeleteUser)) m.HandleFunc("GET /media/{id}", s.handleMedia) m.Handle("GET /static/{file}", s.staticHandler()) // The root domain also serves the superadmin's blog. Literal routes above win // over these wildcards; see reservedPageSlugs for the names that are shadowed. s.blogRoutes(m, s.hostBlog) return s.session(m) } // blogRoutes registers the public blog pages; wrap resolves the blog into the request context. func (s *Server) blogRoutes(m *http.ServeMux, wrap func(http.HandlerFunc) http.HandlerFunc) { m.HandleFunc("GET /{$}", wrap(s.handleBlogHome)) m.HandleFunc("GET /feed.xml", wrap(s.handleBlogFeed)) m.HandleFunc("GET /{page}", wrap(s.handleBlogPage)) m.HandleFunc("GET /{page}/{post}", wrap(s.handleBlogPost)) } 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) return m } func (s *Server) staticHandler() http.Handler { fs := http.StripPrefix("/static/", http.FileServer(http.FS(s.tpl.AssetsFS()))) return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if !s.cfg.Dev { w.Header().Set("Cache-Control", "public, max-age=3600") } fs.ServeHTTP(w, r) }) }