weiro/services/pages/service.go
Leon Mika 1edcd7686c feat(pages): add pages service layer
Implements the pages service with ListPages, GetPage, CreatePage,
UpdatePage, DeletePage, and ReorderPages methods. Wires the service
into the service registry and generalises SlugConflictError message.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 18:01:36 +11:00

190 lines
4.1 KiB
Go

package pages
import (
"context"
"time"
"lmika.dev/lmika/weiro/models"
"lmika.dev/lmika/weiro/providers/db"
"lmika.dev/lmika/weiro/services/publisher"
)
type CreatePageParams struct {
GUID string `form:"guid" json:"guid"`
Title string `form:"title" json:"title"`
Slug string `form:"slug" json:"slug"`
Body string `form:"body" json:"body"`
PageType int `form:"page_type" json:"page_type"`
ShowInNav bool `form:"show_in_nav" json:"show_in_nav"`
}
type Service struct {
db *db.Provider
publisher *publisher.Queue
}
func New(db *db.Provider, publisher *publisher.Queue) *Service {
return &Service{db: db, publisher: publisher}
}
func (s *Service) ListPages(ctx context.Context) ([]*models.Page, error) {
site, ok := models.GetSite(ctx)
if !ok {
return nil, models.SiteRequiredError
}
return s.db.SelectPagesOfSite(ctx, site.ID)
}
func (s *Service) GetPage(ctx context.Context, id int64) (*models.Page, error) {
site, ok := models.GetSite(ctx)
if !ok {
return nil, models.SiteRequiredError
}
page, err := s.db.SelectPage(ctx, id)
if err != nil {
return nil, err
}
if page.SiteID != site.ID {
return nil, models.NotFoundError
}
return page, nil
}
func (s *Service) CreatePage(ctx context.Context, params CreatePageParams) (*models.Page, error) {
site, ok := models.GetSite(ctx)
if !ok {
return nil, models.SiteRequiredError
}
now := time.Now()
slug := params.Slug
if slug == "" {
slug = models.GeneratePageSlug(params.Title)
}
// Check slug collision
if _, err := s.db.SelectPageBySlugAndSite(ctx, site.ID, slug); err == nil {
return nil, models.SlugConflictError
} else if !db.ErrorIsNoRows(err) {
return nil, err
}
// Determine sort order: place at end
existingPages, err := s.db.SelectPagesOfSite(ctx, site.ID)
if err != nil {
return nil, err
}
sortOrder := len(existingPages)
page := &models.Page{
SiteID: site.ID,
GUID: params.GUID,
Title: params.Title,
Slug: slug,
Body: params.Body,
PageType: params.PageType,
ShowInNav: params.ShowInNav,
SortOrder: sortOrder,
CreatedAt: now,
UpdatedAt: now,
}
if page.GUID == "" {
page.GUID = models.NewNanoID()
}
if err := s.db.SavePage(ctx, page); err != nil {
return nil, err
}
s.publisher.Queue(site)
return page, nil
}
func (s *Service) UpdatePage(ctx context.Context, id int64, params CreatePageParams) (*models.Page, error) {
site, ok := models.GetSite(ctx)
if !ok {
return nil, models.SiteRequiredError
}
page, err := s.db.SelectPage(ctx, id)
if err != nil {
return nil, err
}
if page.SiteID != site.ID {
return nil, models.NotFoundError
}
slug := params.Slug
if slug == "" {
slug = models.GeneratePageSlug(params.Title)
}
// Check slug collision (exclude self)
if existing, err := s.db.SelectPageBySlugAndSite(ctx, site.ID, slug); err == nil && existing.ID != page.ID {
return nil, models.SlugConflictError
} else if err != nil && !db.ErrorIsNoRows(err) {
return nil, err
}
page.Title = params.Title
page.Slug = slug
page.Body = params.Body
page.PageType = params.PageType
page.ShowInNav = params.ShowInNav
page.UpdatedAt = time.Now()
if err := s.db.SavePage(ctx, page); err != nil {
return nil, err
}
s.publisher.Queue(site)
return page, nil
}
func (s *Service) DeletePage(ctx context.Context, id int64) error {
site, ok := models.GetSite(ctx)
if !ok {
return models.SiteRequiredError
}
page, err := s.db.SelectPage(ctx, id)
if err != nil {
return err
}
if page.SiteID != site.ID {
return models.NotFoundError
}
if err := s.db.DeletePage(ctx, id); err != nil {
return err
}
s.publisher.Queue(site)
return nil
}
func (s *Service) ReorderPages(ctx context.Context, pageIDs []int64) error {
site, ok := models.GetSite(ctx)
if !ok {
return models.SiteRequiredError
}
// Verify all pages belong to this site
for i, id := range pageIDs {
page, err := s.db.SelectPage(ctx, id)
if err != nil {
return err
}
if page.SiteID != site.ID {
return models.NotFoundError
}
if err := s.db.UpdatePageSortOrder(ctx, id, i); err != nil {
return err
}
}
s.publisher.Queue(site)
return nil
}