179 lines
4.1 KiB
Go
179 lines
4.1 KiB
Go
package posts
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"github.com/PuerkitoBio/goquery"
|
|
"github.com/Southclaws/fault"
|
|
"html/template"
|
|
"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"
|
|
"resty.dev/v3"
|
|
"time"
|
|
)
|
|
|
|
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) ListPostOfSite(ctx context.Context, site models.Site) ([]models.Post, error) {
|
|
return s.db.ListPostsOfSite(ctx, site.ID)
|
|
}
|
|
|
|
func (s *Service) GetPost(ctx context.Context, id int) (models.Post, error) {
|
|
post, err := s.db.GetPost(ctx, int64(id))
|
|
if err != nil {
|
|
return models.Post{}, err
|
|
}
|
|
|
|
return post, nil
|
|
}
|
|
|
|
func (s *Service) DeletePost(ctx context.Context, site models.Site, id int) error {
|
|
post, err := s.db.GetPost(ctx, int64(id))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := s.db.DeletePost(ctx, int64(id)); err != nil {
|
|
return err
|
|
}
|
|
|
|
return s.jobs.Queue(ctx, s.sb.DeletePost(site, post))
|
|
}
|
|
|
|
func (s *Service) Create(ctx context.Context, site models.Site, req NewPost) (models.Post, error) {
|
|
post := models.Post{
|
|
SiteID: site.ID,
|
|
Title: req.Title,
|
|
Body: req.Body,
|
|
State: models.PostStatePublished,
|
|
PublishDate: time.Now(),
|
|
}
|
|
|
|
if err := s.Save(ctx, site, &post); err != nil {
|
|
return models.Post{}, err
|
|
}
|
|
|
|
return post, nil
|
|
}
|
|
|
|
func (s *Service) CreateLinkPost(ctx context.Context, site models.Site, req NewLinkPost) (models.Post, error) {
|
|
fl, err := s.fetchLinkedPage(ctx, req)
|
|
if err != nil {
|
|
return models.Post{}, err
|
|
}
|
|
|
|
var bodyTemplate bytes.Buffer
|
|
if err := linkedPostTemplate.Execute(&bodyTemplate, fl); err != nil {
|
|
return models.Post{}, err
|
|
}
|
|
|
|
post := models.Post{
|
|
SiteID: site.ID,
|
|
Title: fl.LinkTitle,
|
|
Body: bodyTemplate.String(),
|
|
}
|
|
|
|
return post, nil
|
|
}
|
|
|
|
func (s *Service) Save(ctx context.Context, site models.Site, post *models.Post) error {
|
|
post.SiteID = site.ID
|
|
|
|
if post.ID == 0 {
|
|
post.CreatedAt = time.Now()
|
|
post.UpdatedAt = time.Now()
|
|
if err := s.db.InsertPost(ctx, post); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
post.UpdatedAt = time.Now()
|
|
if err := s.db.UpdatePost(ctx, post); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return s.jobs.Queue(ctx, s.sb.WritePost(site, *post))
|
|
}
|
|
|
|
func (s *Service) fetchLinkedPage(ctx context.Context, req NewLinkPost) (fl fetchedLink, err error) {
|
|
client := resty.New()
|
|
defer client.Close()
|
|
|
|
// Fetch the linked site
|
|
res, err := client.R().WithContext(ctx).Get(req.LinkURL)
|
|
if err != nil {
|
|
return fetchedLink{}, err
|
|
} else if res.StatusCode() != 200 {
|
|
return fetchedLink{}, fault.Newf("page returns non-200 status code %d", res.StatusCode())
|
|
}
|
|
|
|
if len(res.RedirectHistory()) > 0 {
|
|
fl.LinkURL = res.RedirectHistory()[len(res.RedirectHistory())-1].URL
|
|
} else {
|
|
fl.LinkURL = req.LinkURL
|
|
}
|
|
|
|
resDom, err := goquery.NewDocumentFromReader(bytes.NewReader(res.Bytes()))
|
|
if err != nil {
|
|
return fetchedLink{}, err
|
|
}
|
|
|
|
fl.LinkTitle = resDom.Find("title").Text()
|
|
|
|
// Fetch the via site
|
|
if req.ViaURL != "" {
|
|
if res, err := client.R().WithContext(ctx).Get(req.ViaURL); err == nil && res.StatusCode() == 200 {
|
|
if len(res.RedirectHistory()) > 0 {
|
|
fl.ViaURL = res.RedirectHistory()[len(res.RedirectHistory())-1].URL
|
|
} else {
|
|
fl.ViaURL = req.LinkURL
|
|
}
|
|
|
|
if viaDom, err := goquery.NewDocumentFromReader(bytes.NewReader(res.Bytes())); err == nil {
|
|
fl.ViaTitle = viaDom.Find("title").Text()
|
|
}
|
|
}
|
|
}
|
|
return fl, nil
|
|
}
|
|
|
|
type NewPost struct {
|
|
Title string `json:"title" form:"title"`
|
|
Body string `json:"body" form:"body"`
|
|
Params map[string]string `json:"params" form:"params"`
|
|
}
|
|
|
|
type NewLinkPost struct {
|
|
LinkURL string `json:"title" form:"link_url"`
|
|
ViaURL string `json:"body" form:"via_url"`
|
|
}
|
|
|
|
type fetchedLink struct {
|
|
LinkURL string
|
|
LinkTitle string
|
|
ViaURL string
|
|
ViaTitle string
|
|
}
|
|
|
|
var linkedPostTemplate = template.Must(template.New("").Parse(`🔗 [{{.LinkTitle}}]({{.LinkURL}})
|
|
|
|
{{if .ViaURL}}Via: [{{.ViaTitle}}]({{.ViaURL}}){{end}}`))
|