2023-06-25 19:32:36 +00:00
|
|
|
package web
|
|
|
|
|
|
|
|
import (
|
2023-07-20 17:49:52 +00:00
|
|
|
"bytes"
|
|
|
|
"encoding/xml"
|
|
|
|
"net/url"
|
2023-06-25 19:32:36 +00:00
|
|
|
"owl-blogs/app"
|
2023-07-20 17:49:52 +00:00
|
|
|
"owl-blogs/app/repository"
|
|
|
|
"owl-blogs/domain/model"
|
|
|
|
"sort"
|
|
|
|
"time"
|
2023-06-25 19:32:36 +00:00
|
|
|
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
|
|
)
|
|
|
|
|
2023-07-20 17:49:52 +00:00
|
|
|
type RSS struct {
|
|
|
|
XMLName xml.Name `xml:"rss"`
|
|
|
|
Version string `xml:"version,attr"`
|
2023-07-21 19:59:25 +00:00
|
|
|
Atom string `xml:"xmlns:atom,attr"`
|
2023-07-20 17:49:52 +00:00
|
|
|
Channel RSSChannel `xml:"channel"`
|
|
|
|
}
|
|
|
|
|
2023-07-21 19:40:32 +00:00
|
|
|
type RSSDescription struct {
|
|
|
|
XMLName xml.Name `xml:"description"`
|
|
|
|
Text string `xml:",cdata"`
|
|
|
|
}
|
|
|
|
|
2023-07-21 19:59:25 +00:00
|
|
|
type AtomLink struct {
|
|
|
|
Href string `xml:"href,attr"`
|
|
|
|
Rel string `xml:"rel,attr,omitempty"`
|
|
|
|
Type string `xml:"type,attr,omitempty"`
|
|
|
|
Title string `xml:"title,attr,omitempty"`
|
|
|
|
}
|
|
|
|
|
2023-07-20 17:49:52 +00:00
|
|
|
type RSSChannel struct {
|
2023-07-21 19:40:32 +00:00
|
|
|
Title string `xml:"title"`
|
|
|
|
Link string `xml:"link"`
|
2023-07-21 19:59:25 +00:00
|
|
|
AtomLinks []AtomLink `xml:"atom:link"`
|
2023-07-21 19:40:32 +00:00
|
|
|
Description RSSDescription `xml:"description"`
|
2023-07-21 20:09:48 +00:00
|
|
|
PubDate string `xml:"pubDate"`
|
|
|
|
LastBuild string `xml:"lastBuildDate"`
|
|
|
|
Generator string `xml:"generator"`
|
2023-07-21 19:40:32 +00:00
|
|
|
Items []RSSItem `xml:"item"`
|
2023-07-20 17:49:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type RSSItem struct {
|
2023-07-21 19:40:32 +00:00
|
|
|
Guid string `xml:"guid"`
|
|
|
|
Title string `xml:"title"`
|
|
|
|
Link string `xml:"link"`
|
|
|
|
PubDate string `xml:"pubDate"`
|
|
|
|
Description RSSDescription `xml:"description"`
|
2023-07-20 17:49:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func RenderRSSFeed(config model.SiteConfig, entries []model.Entry) (string, error) {
|
|
|
|
|
|
|
|
rss := RSS{
|
|
|
|
Version: "2.0",
|
2023-07-21 19:59:25 +00:00
|
|
|
Atom: "http://www.w3.org/2005/Atom",
|
2023-07-20 17:49:52 +00:00
|
|
|
Channel: RSSChannel{
|
2023-07-21 19:40:32 +00:00
|
|
|
Title: config.Title,
|
|
|
|
Link: config.FullUrl,
|
2023-07-21 19:59:25 +00:00
|
|
|
AtomLinks: []AtomLink{
|
|
|
|
{
|
|
|
|
Href: config.FullUrl + "/index.xml",
|
|
|
|
Rel: "self",
|
|
|
|
Type: "application/rss+xml",
|
|
|
|
},
|
|
|
|
},
|
2023-07-21 19:40:32 +00:00
|
|
|
Description: RSSDescription{
|
|
|
|
Text: config.SubTitle,
|
|
|
|
},
|
2023-07-21 20:09:48 +00:00
|
|
|
PubDate: time.Now().Format(time.RFC1123Z),
|
|
|
|
LastBuild: time.Now().Format(time.RFC1123Z),
|
|
|
|
Generator: "owl-blogs",
|
|
|
|
Items: make([]RSSItem, 0),
|
2023-07-20 17:49:52 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, entry := range entries {
|
|
|
|
content := entry.Content()
|
2023-07-21 19:40:32 +00:00
|
|
|
entryUrl, _ := url.JoinPath(config.FullUrl, "/posts/", url.PathEscape(entry.ID()), "/")
|
|
|
|
|
2023-07-20 17:49:52 +00:00
|
|
|
rss.Channel.Items = append(rss.Channel.Items, RSSItem{
|
2023-07-21 19:40:32 +00:00
|
|
|
Guid: entryUrl,
|
|
|
|
Title: entry.Title(),
|
|
|
|
Link: entryUrl,
|
|
|
|
PubDate: entry.PublishedAt().Format(time.RFC1123Z),
|
|
|
|
Description: RSSDescription{
|
|
|
|
Text: string(content),
|
|
|
|
},
|
2023-07-20 17:49:52 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
buf := new(bytes.Buffer)
|
2023-07-21 19:40:32 +00:00
|
|
|
encoder := xml.NewEncoder(buf)
|
|
|
|
encoder.Indent("", " ")
|
|
|
|
err := encoder.Encode(rss)
|
2023-07-20 17:49:52 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return xml.Header + buf.String(), nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-06-25 19:32:36 +00:00
|
|
|
type RSSHandler struct {
|
2023-07-20 17:49:52 +00:00
|
|
|
configRepo repository.ConfigRepository
|
|
|
|
entrySvc *app.EntryService
|
2023-06-25 19:32:36 +00:00
|
|
|
}
|
|
|
|
|
2023-07-20 17:49:52 +00:00
|
|
|
func NewRSSHandler(entryService *app.EntryService, configRepo repository.ConfigRepository) *RSSHandler {
|
|
|
|
return &RSSHandler{entrySvc: entryService, configRepo: configRepo}
|
2023-06-25 19:32:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (h *RSSHandler) Handle(c *fiber.Ctx) error {
|
2023-07-20 17:49:52 +00:00
|
|
|
c.Set(fiber.HeaderContentType, fiber.MIMEApplicationXML)
|
|
|
|
|
|
|
|
siteConfig := getSiteConfig(h.configRepo)
|
|
|
|
|
|
|
|
entries, err := h.entrySvc.FindAllByType(&siteConfig.PrimaryListInclude, true, false)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// sort entries by date descending
|
|
|
|
sort.Slice(entries, func(i, j int) bool {
|
|
|
|
return entries[i].PublishedAt().After(*entries[j].PublishedAt())
|
|
|
|
})
|
|
|
|
|
|
|
|
rss, err := RenderRSSFeed(siteConfig, entries)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.SendString(rss)
|
2023-06-25 19:32:36 +00:00
|
|
|
}
|