From f0eaf46755e04b806e2df07a2fabd4060a72d54c Mon Sep 17 00:00:00 2001 From: grm Date: Sat, 12 Sep 2026 11:43:14 +0300 Subject: Serve the superadmin's blog on the root domain The base domain (and www.) now serves a regular blog owned by the first superadmin, created automatically on startup, alongside the management routes. Literal management paths take precedence over the blog's page wildcards, and the slugs they would shadow are reserved. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A --- cmd/blogspace/main.go | 23 +++++++++++++++++++++++ cmd/blogspace/seed.go | 16 ++++++++++++++++ 2 files changed, 39 insertions(+) (limited to 'cmd') diff --git a/cmd/blogspace/main.go b/cmd/blogspace/main.go index 3a60bd4..cf240a8 100644 --- a/cmd/blogspace/main.go +++ b/cmd/blogspace/main.go @@ -65,6 +65,9 @@ func run(ctx context.Context, cfg *config.Config, cmd string) error { if err := bootstrapSuperadmin(ctx, cfg, st); err != nil { return err } + if err := ensureRootBlog(ctx, cfg, st); err != nil { + return err + } return serve(ctx, cfg, st) default: return fmt.Errorf("unknown command %q (serve|seed|migrate)", cmd) @@ -119,6 +122,26 @@ func bootstrapSuperadmin(ctx context.Context, cfg *config.Config, st *store.Stor return nil } +// ensureRootBlog makes sure the blog served on the base domain exists; it is owned by the first superadmin. +func ensureRootBlog(ctx context.Context, cfg *config.Config, st *store.Store) error { + _, err := st.BlogBySubdomain(ctx, config.RootSubdomain) + if err == nil { + return nil + } + if !errors.Is(err, store.ErrNotFound) { + return err + } + admin, err := st.FirstSuperadmin(ctx) + if err != nil { + return fmt.Errorf("root blog needs a superadmin: %w", err) + } + if _, err := st.CreateBlog(ctx, admin.ID, config.RootSubdomain, cfg.BaseDomain); err != nil { + return fmt.Errorf("create root blog: %w", err) + } + log.Printf("created root blog %s owned by %q", cfg.BaseDomain, admin.Username) + return nil +} + func serve(ctx context.Context, cfg *config.Config, st *store.Store) error { srv := &http.Server{ Addr: cfg.Addr, diff --git a/cmd/blogspace/seed.go b/cmd/blogspace/seed.go index 847324c..010eff5 100644 --- a/cmd/blogspace/seed.go +++ b/cmd/blogspace/seed.go @@ -24,6 +24,9 @@ func seed(ctx context.Context, cfg *config.Config, st *store.Store) error { if err := bootstrapSuperadmin(ctx, cfg, st); err != nil { return err } + if err := ensureRootBlog(ctx, cfg, st); err != nil { + return err + } if _, err := st.UserByUsername(ctx, "alice"); err == nil { log.Println("seed: alice already exists, nothing to do") return nil @@ -103,6 +106,19 @@ func seed(ctx context.Context, cfg *config.Config, st *store.Store) error { return err } } + // a welcome post on the root blog (the superadmin's own blog on the base domain) + root, err := st.BlogBySubdomain(ctx, config.RootSubdomain) + if err != nil { + return err + } + rootHome, err := st.HomePage(ctx, root.ID) + if err != nil { + return err + } + welcome := "This is the blog on the **root domain**, owned by the superadmin. It works like every other blog.\n\nOther blogs live on subdomains, for example [alice](" + cfg.BlogURL("alice") + ")." + if _, err := st.CreatePost(ctx, &store.Post{PageID: rootHome.ID, Title: "Welcome to Blogspace", Slug: "welcome", BodyMD: welcome, BodyHTML: markdown.Render(welcome), Published: true}); err != nil { + return err + } log.Printf("seed: superadmin %q (password %q in dev), blogger alice / alicealice at %s", cfg.SuperadminUsername, "admin", cfg.BlogURL("alice")) return nil } -- cgit v1.2.3