From 68aa9c0e1398b8e10d1bf8e07a5549461ccf54a4 Mon Sep 17 00:00:00 2001 From: Leon Mika Date: Mon, 17 Feb 2025 21:41:36 +1100 Subject: [PATCH] Added site previewing This will generate a local version of the Hugo site and serve it via the server --- config/config.go | 5 ++++ main.go | 6 +++- models/theme.go | 3 ++ providers/hugo/config.go | 1 + providers/hugo/provider.go | 50 +++++++++++++++++++++------------ services/sitebuilder/pages.go | 5 ++-- services/sitebuilder/posts.go | 2 +- services/sitebuilder/publish.go | 15 ++++------ services/sitebuilder/service.go | 7 ++++- 9 files changed, 61 insertions(+), 33 deletions(-) diff --git a/config/config.go b/config/config.go index e7bd671..518e082 100644 --- a/config/config.go +++ b/config/config.go @@ -13,6 +13,7 @@ type Config struct { DataStagingDir string `env:"DATA_STAGING_DIR,default=staging"` DataScratchDir string `env:"DATA_SCRATCH_DIR,default=scratch"` + DataPreviewDir string `env:"DATA_PREVIEW_DIR,default=preview"` } func Load() (cfg Config, err error) { @@ -27,6 +28,10 @@ func (c Config) StagingDir() string { return filepath.Join(c.DataDir, c.DataStagingDir) } +func (c Config) PreviewDir() string { + return filepath.Join(c.DataDir, c.DataPreviewDir) +} + func (c Config) ScratchDir() string { return filepath.Join(c.DataDir, c.DataScratchDir) } diff --git a/main.go b/main.go index 5ff1f4b..fea03d0 100644 --- a/main.go +++ b/main.go @@ -72,7 +72,7 @@ func main() { return } - hugoProvider, err := hugo.New(cfg.StagingDir(), cfg.ScratchDir()) + hugoProvider, err := hugo.New(cfg.StagingDir(), cfg.PreviewDir(), cfg.ScratchDir()) if err != nil { log.Fatal(err) } @@ -129,6 +129,10 @@ func main() { app.Post("/sites", siteHandlers.Create) app.Get("/sites/:siteId", siteHandlers.Show) + app.Use("/preview", static.New(cfg.PreviewDir(), static.Config{ + Browse: true, + })) + sr := app.Group("/sites/:siteId") sr.Use(siteHandlers.WithSite()) sr.Post("/rebuild", siteHandlers.Rebuild) diff --git a/models/theme.go b/models/theme.go index e832748..69e9d81 100644 --- a/models/theme.go +++ b/models/theme.go @@ -8,6 +8,9 @@ type ThemeMeta struct { // Indicates that this theme prefers posts have titles. PreferTitle bool + // Indicates that the theme doesn't automatically put titles on pages + AddTitleToPages bool + // Page bundle for "blog" posts BlogPostBundle string `json:"post_dir"` } diff --git a/providers/hugo/config.go b/providers/hugo/config.go index e888090..e28a736 100644 --- a/providers/hugo/config.go +++ b/providers/hugo/config.go @@ -5,6 +5,7 @@ type hugoConfig struct { LanguageCode string `yaml:"languageCode"` Title string `yaml:"title"` Theme string `yaml:"theme"` + CanonifyURLs bool `yaml:"canonifyURLs,omitempty"` Markup hugoConfigMarkup `yaml:"markup"` } diff --git a/providers/hugo/provider.go b/providers/hugo/provider.go index 7fc516a..0cf94be 100644 --- a/providers/hugo/provider.go +++ b/providers/hugo/provider.go @@ -2,6 +2,7 @@ package hugo import ( "context" + "fmt" "gopkg.in/yaml.v3" "lmika.dev/lmika/hugo-cms/models" "log" @@ -12,12 +13,14 @@ import ( type Provider struct { stagingDir string + previewDir string scratchDir string } -func New(stagingDir, scratchDir string) (*Provider, error) { +func New(stagingDir, previewDir, scratchDir string) (*Provider, error) { return &Provider{ stagingDir: stagingDir, + previewDir: previewDir, scratchDir: scratchDir, }, nil } @@ -47,42 +50,53 @@ func (p *Provider) NewSite(ctx context.Context, site models.Site) error { return nil } -func (p *Provider) PublishSite(ctx context.Context, site models.Site, target models.PublishTarget) (outDir string, clean func(), err error) { - if err := os.MkdirAll(p.scratchDir, 0755); err != nil { - return "", nil, err +func (p *Provider) PreviewSite(ctx context.Context, site models.Site) (outDir string, err error) { + previewTarget := models.PublishTarget{ + URL: fmt.Sprintf("http://localhost:3000/preview/%s", site.Name), } - outDir, err = os.MkdirTemp(p.scratchDir, site.Name+"-*") + return p.publishSiteAt(ctx, p.previewDir, site, previewTarget, "hugoPreview.yaml") +} + +func (p *Provider) PublishSite(ctx context.Context, site models.Site, target models.PublishTarget) (outDir string, err error) { + return p.publishSiteAt(ctx, p.scratchDir, site, target, "hugo.yaml") +} + +func (p *Provider) publishSiteAt(ctx context.Context, dir string, site models.Site, target models.PublishTarget, configFile string) (outDir string, err error) { + baseSiteDir, err := filepath.Abs(p.SiteStagingDir(site, BaseSiteDir)) if err != nil { - return "", nil, err - } - clean = func() { - os.RemoveAll(outDir) + return "", err } - outDir, err = filepath.Abs(outDir) + outDir, err = filepath.Abs(filepath.Join(dir, site.Name)) if err != nil { - return "", nil, err + return "", err + } + + if err := os.MkdirAll(outDir, 0755); err != nil { + return "", err } cmd := exec.CommandContext(ctx, "hugo", - "--source", p.SiteStagingDir(site, BaseSiteDir), + "--source", baseSiteDir, "--destination", outDir, + "--config", filepath.Join(baseSiteDir, configFile), "--baseURL", target.URL) cmd.Stderr = os.Stderr cmd.Stdout = os.Stdout if err := cmd.Run(); err != nil { - return "", clean, err + return "", err } - return outDir, clean, nil + return outDir, nil } -func (p *Provider) ReconfigureSite(ctx context.Context, site models.Site) error { +func (p *Provider) ReconfigureSite(ctx context.Context, configBase string, site models.Site) error { hugoCfg := hugoConfig{ Title: site.Title, LanguageCode: "en", Theme: site.Theme, + CanonifyURLs: configBase == "hugoPreview", Markup: hugoConfigMarkup{ Goldmark: hugoGoldmarkConfig{ Renderer: hugoGoldmarkRendererConfig{ @@ -97,12 +111,12 @@ func (p *Provider) ReconfigureSite(ctx context.Context, site models.Site) error return err } - if err := os.WriteFile(filepath.Join(p.SiteStagingDir(site, BaseSiteDir), "hugo.yaml"), ymlBytes, 0644); err != nil { + if err := os.WriteFile(filepath.Join(p.SiteStagingDir(site, BaseSiteDir), configBase+".yaml"), ymlBytes, 0644); err != nil { return err } - if _, err := os.Stat(filepath.Join(p.SiteStagingDir(site, BaseSiteDir), "hugo.toml")); err == nil { - if err := os.Remove(filepath.Join(p.SiteStagingDir(site, BaseSiteDir), "hugo.toml")); err != nil { + if _, err := os.Stat(filepath.Join(p.SiteStagingDir(site, BaseSiteDir), configBase+".toml")); err == nil { + if err := os.Remove(filepath.Join(p.SiteStagingDir(site, BaseSiteDir), configBase+".toml")); err != nil { return err } } diff --git a/services/sitebuilder/pages.go b/services/sitebuilder/pages.go index 7acb5a6..c253c59 100644 --- a/services/sitebuilder/pages.go +++ b/services/sitebuilder/pages.go @@ -55,7 +55,7 @@ func (s *Service) DeletePage(site models.Site, page models.Page) models.Job { if err != nil { return err } - + themeMeta, ok := s.themes.Lookup(site.Theme) if !ok { return fmt.Errorf("theme %s not found in themes", site.Theme) @@ -130,10 +130,9 @@ func (s *Service) writePage(bi pageBuildInfo, page models.Page) error { frontMatter := map[string]any{ "date": page.PublishDate.Format(time.RFC3339), } + if page.Title != "" { frontMatter["title"] = page.Title - } else if bi.themeMeta.PreferTitle { - frontMatter["title"] = page.PublishDate.Format(time.ANSIC) } return s.writeMarkdownFile(postFilename, frontMatter, page.Body) diff --git a/services/sitebuilder/posts.go b/services/sitebuilder/posts.go index 52a5d17..e67a5e2 100644 --- a/services/sitebuilder/posts.go +++ b/services/sitebuilder/posts.go @@ -156,7 +156,7 @@ func (s *Service) pageFilename(bi pageBuildInfo, page models.Page) string { isLeafBundle := true thisBundleInfo := bi.bundleInfo[bi.bundle.ID] - if thisBundleInfo.PageCount > 1 { + if thisBundleInfo.PageCount > 1 || bi.bundle.Name == models.RootBundleName { isLeafBundle = false isIndex = thisBundleInfo.IndexPageID == page.ID } else { diff --git a/services/sitebuilder/publish.go b/services/sitebuilder/publish.go index 509d86e..e730087 100644 --- a/services/sitebuilder/publish.go +++ b/services/sitebuilder/publish.go @@ -10,13 +10,17 @@ func (s *Service) Publish(site models.Site) models.Job { Do: func(ctx context.Context) error { s.signalSiteBuildingStarted(ctx, site) defer s.signalSiteBuildingFinished(ctx, site) - + return s.publish(ctx, site) }, } } func (s *Service) publish(ctx context.Context, site models.Site) error { + if _, err := s.hugo.PreviewSite(ctx, site); err != nil { + return err + } + targets, err := s.db.GetPublishTargets(ctx, site.ID) if err != nil { return err @@ -31,14 +35,7 @@ func (s *Service) publish(ctx context.Context, site models.Site) error { } func (s *Service) publishTarget(ctx context.Context, site models.Site, target models.PublishTarget) error { - outDir, cleanFn, err := s.hugo.PublishSite(ctx, site, target) - //defer func() { - // if cleanFn != nil { - // cleanFn() - // } - //}() - _ = cleanFn - + outDir, err := s.hugo.PublishSite(ctx, site, target) if err != nil { return err } diff --git a/services/sitebuilder/service.go b/services/sitebuilder/service.go index dabe65d..929af90 100644 --- a/services/sitebuilder/service.go +++ b/services/sitebuilder/service.go @@ -172,9 +172,14 @@ func (s *Service) createSite(ctx context.Context, site models.Site) error { return err } - if err := s.hugo.ReconfigureSite(ctx, site); err != nil { + if err := s.hugo.ReconfigureSite(ctx, "hugo", site); err != nil { return err } + + if err := s.hugo.ReconfigureSite(ctx, "hugoPreview", site); err != nil { + return err + } + return nil }