feat: add category pages and per-category feeds to site builder

Extend the publishing pipeline to generate category index pages,
per-category archive pages, per-category RSS/JSON feeds, and display
categories on individual post pages and post lists.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Leon Mika 2026-03-18 21:51:19 +11:00
parent 4c2ce7272d
commit 6c69131b03
10 changed files with 329 additions and 61 deletions

View file

@ -8,7 +8,7 @@ import (
"lmika.dev/lmika/weiro/providers/db"
)
// PostIter returns a post iterator which returns posts in reverse chronological order.
// postIter returns a post iterator which returns posts in reverse chronological order.
func (s *Publisher) postIter(ctx context.Context, site int64) iter.Seq[models.Maybe[*models.Post]] {
return func(yield func(models.Maybe[*models.Post]) bool) {
paging := db.PagingParams{Offset: 0, Limit: 50}
@ -39,3 +39,26 @@ func (s *Publisher) postIter(ctx context.Context, site int64) iter.Seq[models.Ma
}
}
}
// postIterByCategory returns a post iterator for posts in a specific category.
func (s *Publisher) postIterByCategory(ctx context.Context, categoryID int64) iter.Seq[models.Maybe[*models.Post]] {
return func(yield func(models.Maybe[*models.Post]) bool) {
paging := db.PagingParams{Offset: 0, Limit: 50}
for {
page, err := s.db.SelectPostsOfCategory(ctx, categoryID, paging)
if err != nil {
yield(models.Maybe[*models.Post]{Err: err})
return
}
if len(page) == 0 {
return
}
for _, post := range page {
if !yield(models.Maybe[*models.Post]{Value: post}) {
return
}
}
paging.Offset += paging.Limit
}
}
}

View file

@ -46,6 +46,24 @@ func (p *Publisher) Publish(ctx context.Context, site models.Site) error {
return err
}
// Fetch categories with counts
cats, err := p.db.SelectCategoriesOfSite(ctx, site.ID)
if err != nil {
return err
}
var catsWithCounts []models.CategoryWithCount
for _, cat := range cats {
count, err := p.db.CountPostsOfCategory(ctx, cat.ID)
if err != nil {
return err
}
catsWithCounts = append(catsWithCounts, models.CategoryWithCount{
Category: *cat,
PostCount: int(count),
DescriptionBrief: models.BriefDescription(cat.Description),
})
}
for _, target := range targets {
if !target.Enabled {
continue
@ -56,8 +74,15 @@ func (p *Publisher) Publish(ctx context.Context, site models.Site) error {
PostIter: func(ctx context.Context) iter.Seq[models.Maybe[*models.Post]] {
return p.postIter(ctx, site.ID)
},
BaseURL: target.BaseURL,
Uploads: uploads,
BaseURL: target.BaseURL,
Uploads: uploads,
Categories: catsWithCounts,
PostIterByCategory: func(ctx context.Context, categoryID int64) iter.Seq[models.Maybe[*models.Post]] {
return p.postIterByCategory(ctx, categoryID)
},
CategoriesOfPost: func(ctx context.Context, postID int64) ([]*models.Category, error) {
return p.db.SelectCategoriesOfPost(ctx, postID)
},
OpenUpload: func(u models.Upload) (io.ReadCloser, error) {
return p.up.OpenUpload(site, u)
},