184 lines
4.1 KiB
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
|
||
|
}
|