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: name, 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 }