hugo-cms/services/pages/services.go

184 lines
4.1 KiB
Go

package pages
import (
"context"
"errors"
"lmika.dev/lmika/hugo-cms/models"
"lmika.dev/lmika/hugo-cms/providers/db"
"lmika.dev/lmika/hugo-cms/services/jobs"
"lmika.dev/lmika/hugo-cms/services/sitebuilder"
"lmika.dev/pkg/modash/moslice"
"strings"
"time"
"unicode"
)
type Service struct {
db *db.DB
sb *sitebuilder.Service
jobs *jobs.Service
}
func New(
db *db.DB,
sb *sitebuilder.Service,
jobs *jobs.Service,
) *Service {
return &Service{
db: db,
sb: sb,
jobs: jobs,
}
}
func (s *Service) ListPagesOfSite(ctx context.Context, site models.Site) ([]models.Page, error) {
return s.db.ListPagesOfSite(ctx, site.ID)
}
func (s *Service) GetPage(ctx context.Context, id int) (models.Page, error) {
post, err := s.db.GetPage(ctx, int64(id))
if err != nil {
return models.Page{}, err
}
return post, nil
}
func (s *Service) DeletePage(ctx context.Context, site models.Site, id int) error {
post, err := s.db.GetPage(ctx, int64(id))
if err != nil {
return err
}
if err := s.db.DeletePage(ctx, int64(id)); err != nil {
return err
}
return s.jobs.Queue(ctx, s.sb.DeletePage(site, post))
}
func (s *Service) Create(ctx context.Context, site models.Site, req NewPost) (models.Page, error) {
siteBundles, err := s.db.ListBundles(ctx, site.ID)
if err != nil {
return models.Page{}, err
} else if len(siteBundles) == 0 {
return models.Page{}, errors.New("no bundles found")
}
rootBundle, ok := moslice.FindWhere(siteBundles, func(t models.Bundle) bool {
return t.Name == models.RootBundleName
})
if !ok {
return models.Page{}, errors.New("root bundle not found")
}
publishTime := time.Now()
name := s.normalizePageName(req.Title)
nameProvenance := models.TitleNameProvenance
if name == "" {
// Use the timestamp as the name
name = publishTime.Format("2006-01-02-150405")
nameProvenance = models.DateNameProvenance
}
post := models.Page{
SiteID: site.ID,
BundleID: rootBundle.ID,
Name: s.normalizePageName(req.Title),
NameProvenance: nameProvenance,
Title: req.Title,
Body: req.Body,
State: models.PostStatePublished,
PublishDate: time.Now(),
}
if err := s.save(ctx, site, rootBundle, &post); err != nil {
return models.Page{}, err
}
return post, nil
}
func (s *Service) Update(ctx context.Context, site models.Site, pageID int64, req NewPost) (models.Page, error) {
page, err := s.db.GetPage(ctx, pageID)
if err != nil {
return models.Page{}, err
}
if page.SiteID != site.ID {
return models.Page{}, errors.New("page not found")
}
bundle, err := s.db.GetBundleWithID(ctx, page.BundleID)
if err != nil {
return models.Page{}, err
} else if bundle.SiteID != site.ID {
return models.Page{}, errors.New("page not found")
}
// Update the title if it wasn't set by the user
if page.NameProvenance != models.UserNameProvenance {
if req.Title == "" {
page.Name = page.PublishDate.Format("2006-01-02-150405")
page.NameProvenance = models.DateNameProvenance
} else {
page.Name = s.normalizePageName(req.Title)
page.NameProvenance = models.TitleNameProvenance
}
}
page.Title = req.Title
page.Body = req.Body
if err := s.save(ctx, site, bundle, &page); err != nil {
return models.Page{}, err
}
return page, nil
}
func (s *Service) save(ctx context.Context, site models.Site, bundle models.Bundle, page *models.Page) error {
page.SiteID = site.ID
if page.ID == 0 {
page.CreatedAt = time.Now()
page.UpdatedAt = time.Now()
if err := s.db.InsertPage(ctx, page); err != nil {
return err
}
} else {
page.UpdatedAt = time.Now()
if err := s.db.UpdatePage(ctx, page); err != nil {
return err
}
}
return s.jobs.Queue(ctx, s.sb.WritePage(site, bundle, *page))
}
func (s *Service) normalizePageName(title string) string {
var sb strings.Builder
lastSpace := false
for _, r := range title {
switch {
case unicode.IsSpace(r):
if !lastSpace {
sb.WriteRune('-')
lastSpace = true
}
case unicode.IsNumber(r):
lastSpace = false
sb.WriteRune(r)
case unicode.IsLetter(r):
lastSpace = false
sb.WriteRune(unicode.ToLower(r))
}
}
return sb.String()
}
type NewPost struct {
Title string
Body string
}