process likes from ActivityPub
This commit is contained in:
parent
cba57ba708
commit
9cfbf0b9b7
|
@ -15,6 +15,7 @@ import (
|
|||
"owl-blogs/config"
|
||||
"owl-blogs/domain/model"
|
||||
entrytypes "owl-blogs/entry_types"
|
||||
"owl-blogs/interactions"
|
||||
"owl-blogs/render"
|
||||
"reflect"
|
||||
"time"
|
||||
|
@ -54,21 +55,27 @@ func (cfg *ActivityPubConfig) PrivateKey() *rsa.PrivateKey {
|
|||
}
|
||||
|
||||
type ActivityPubService struct {
|
||||
followersRepo repository.FollowerRepository
|
||||
configRepo repository.ConfigRepository
|
||||
siteConfigServcie *SiteConfigService
|
||||
followersRepo repository.FollowerRepository
|
||||
configRepo repository.ConfigRepository
|
||||
interactionRepository repository.InteractionRepository
|
||||
entryService *EntryService
|
||||
siteConfigServcie *SiteConfigService
|
||||
}
|
||||
|
||||
func NewActivityPubService(
|
||||
followersRepo repository.FollowerRepository,
|
||||
configRepo repository.ConfigRepository,
|
||||
interactionRepository repository.InteractionRepository,
|
||||
entryService *EntryService,
|
||||
siteConfigServcie *SiteConfigService,
|
||||
bus *EventBus,
|
||||
) *ActivityPubService {
|
||||
service := &ActivityPubService{
|
||||
followersRepo: followersRepo,
|
||||
configRepo: configRepo,
|
||||
siteConfigServcie: siteConfigServcie,
|
||||
followersRepo: followersRepo,
|
||||
configRepo: configRepo,
|
||||
interactionRepository: interactionRepository,
|
||||
entryService: entryService,
|
||||
siteConfigServcie: siteConfigServcie,
|
||||
}
|
||||
|
||||
bus.Subscribe(service)
|
||||
|
@ -275,6 +282,51 @@ func (s *ActivityPubService) Accept(act *vocab.Activity) error {
|
|||
return s.sendObject(actor, data)
|
||||
}
|
||||
|
||||
func (s *ActivityPubService) AddLike(sender string, liked string, likeId string) error {
|
||||
entry, err := s.entryService.FindByUrl(liked)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
actor, err := s.GetActor(sender)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var like *interactions.Like
|
||||
interaction, err := s.interactionRepository.FindById(likeId)
|
||||
if err != nil {
|
||||
interaction = &interactions.Like{}
|
||||
}
|
||||
like, ok := interaction.(*interactions.Like)
|
||||
if !ok {
|
||||
return errors.New("existing interaction with same id is not a like")
|
||||
}
|
||||
existing := like.ID() != ""
|
||||
|
||||
likeMeta := interactions.LikeMetaData{
|
||||
SenderUrl: sender,
|
||||
SenderName: actor.Name.String(),
|
||||
}
|
||||
like.SetID(likeId)
|
||||
like.SetMetaData(&likeMeta)
|
||||
like.SetEntryID(entry.ID())
|
||||
like.SetCreatedAt(time.Now())
|
||||
if !existing {
|
||||
return s.interactionRepository.Create(like)
|
||||
} else {
|
||||
return s.interactionRepository.Update(like)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ActivityPubService) RemoveLike(id string) error {
|
||||
interaction, err := s.interactionRepository.FindById(id)
|
||||
if err != nil {
|
||||
interaction = &interactions.Like{}
|
||||
}
|
||||
return s.interactionRepository.Delete(interaction)
|
||||
}
|
||||
|
||||
func (s *ActivityPubService) sendObject(to vocab.Actor, data []byte) error {
|
||||
siteConfig := model.SiteConfig{}
|
||||
apConfig := ActivityPubConfig{}
|
||||
|
@ -325,10 +377,11 @@ func (s *ActivityPubService) sendObject(to vocab.Actor, data []byte) error {
|
|||
*/
|
||||
|
||||
func (svc *ActivityPubService) NotifyEntryCreated(entry model.Entry) {
|
||||
slog.Info("Processing Entry Create for ActivityPub")
|
||||
// limit to notes for now
|
||||
noteEntry, ok := entry.(*entrytypes.Note)
|
||||
if !ok {
|
||||
slog.Info("not an image")
|
||||
slog.Info("not a note")
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"owl-blogs/app/repository"
|
||||
"owl-blogs/domain/model"
|
||||
|
@ -9,17 +10,20 @@ import (
|
|||
)
|
||||
|
||||
type EntryService struct {
|
||||
EntryRepository repository.EntryRepository
|
||||
Bus *EventBus
|
||||
EntryRepository repository.EntryRepository
|
||||
siteConfigServcie *SiteConfigService
|
||||
Bus *EventBus
|
||||
}
|
||||
|
||||
func NewEntryService(
|
||||
entryRepository repository.EntryRepository,
|
||||
siteConfigServcie *SiteConfigService,
|
||||
bus *EventBus,
|
||||
) *EntryService {
|
||||
return &EntryService{
|
||||
EntryRepository: entryRepository,
|
||||
Bus: bus,
|
||||
EntryRepository: entryRepository,
|
||||
siteConfigServcie: siteConfigServcie,
|
||||
Bus: bus,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,6 +74,19 @@ func (s *EntryService) FindById(id string) (model.Entry, error) {
|
|||
return s.EntryRepository.FindById(id)
|
||||
}
|
||||
|
||||
func (s *EntryService) FindByUrl(url string) (model.Entry, error) {
|
||||
cfg, _ := s.siteConfigServcie.GetSiteConfig()
|
||||
if !strings.HasPrefix(url, cfg.FullUrl) {
|
||||
return nil, errors.New("url does not belong to blog")
|
||||
}
|
||||
if strings.HasSuffix(url, "/") {
|
||||
url = url[:len(url)-1]
|
||||
}
|
||||
parts := strings.Split(url, "/")
|
||||
id := parts[len(parts)-1]
|
||||
return s.FindById(id)
|
||||
}
|
||||
|
||||
func (s *EntryService) filterEntries(entries []model.Entry, published bool, drafts bool) []model.Entry {
|
||||
filteredEntries := make([]model.Entry, 0)
|
||||
for _, entry := range entries {
|
||||
|
|
|
@ -14,7 +14,7 @@ func setupService() *app.EntryService {
|
|||
register := app.NewEntryTypeRegistry()
|
||||
register.Register(&test.MockEntry{})
|
||||
repo := infra.NewEntryRepository(db, register)
|
||||
service := app.NewEntryService(repo, app.NewEventBus())
|
||||
service := app.NewEntryService(repo, nil, app.NewEventBus())
|
||||
return service
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ func App(db infra.Database) *web.WebApp {
|
|||
|
||||
interactionRegister := app.NewInteractionTypeRegistry()
|
||||
interactionRegister.Register(&interactions.Webmention{})
|
||||
interactionRegister.Register(&interactions.Like{})
|
||||
|
||||
configRegister := app.NewConfigRegister()
|
||||
|
||||
|
@ -60,13 +61,17 @@ func App(db infra.Database) *web.WebApp {
|
|||
|
||||
// Create Services
|
||||
siteConfigService := app.NewSiteConfigService(configRepo)
|
||||
entryService := app.NewEntryService(entryRepo, eventBus)
|
||||
entryService := app.NewEntryService(entryRepo, siteConfigService, eventBus)
|
||||
binaryService := app.NewBinaryFileService(binRepo)
|
||||
authorService := app.NewAuthorService(authorRepo, siteConfigService)
|
||||
webmentionService := app.NewWebmentionService(
|
||||
siteConfigService, interactionRepo, entryRepo, httpClient, eventBus,
|
||||
)
|
||||
apService := app.NewActivityPubService(followersRepo, configRepo, siteConfigService, eventBus)
|
||||
apService := app.NewActivityPubService(
|
||||
followersRepo, configRepo, interactionRepo,
|
||||
entryService, siteConfigService,
|
||||
eventBus,
|
||||
)
|
||||
|
||||
// setup render functions
|
||||
render.SiteConfigService = siteConfigService
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package interactions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"owl-blogs/domain/model"
|
||||
"owl-blogs/render"
|
||||
)
|
||||
|
||||
type Like struct {
|
||||
model.InteractionBase
|
||||
meta LikeMetaData
|
||||
}
|
||||
|
||||
type LikeMetaData struct {
|
||||
SenderUrl string
|
||||
SenderName string
|
||||
}
|
||||
|
||||
func (i *Like) Content() model.InteractionContent {
|
||||
str, err := render.RenderTemplateToString("interaction/Like", i)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
return model.InteractionContent(str)
|
||||
}
|
||||
|
||||
func (i *Like) MetaData() interface{} {
|
||||
return &i.meta
|
||||
}
|
||||
|
||||
func (i *Like) SetMetaData(metaData interface{}) {
|
||||
i.meta = *metaData.(*LikeMetaData)
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
Liked by <a href="{{.MetaData.SenderUrl}}">
|
||||
{{.MetaData.SenderName}}
|
||||
</a>
|
|
@ -160,26 +160,51 @@ func (s *ActivityPubServer) processFollow(r *http.Request, act *vocab.Activity)
|
|||
}
|
||||
|
||||
func (s *ActivityPubServer) processUndo(r *http.Request, act *vocab.Activity) error {
|
||||
follower := act.Actor.GetID().String()
|
||||
err := s.apService.VerifySignature(r, follower)
|
||||
sender := act.Actor.GetID().String()
|
||||
err := s.apService.VerifySignature(r, sender)
|
||||
|
||||
return vocab.OnObject(act.Object, func(o *vocab.Object) error {
|
||||
if o.Type == vocab.FollowType {
|
||||
if err != nil {
|
||||
slog.Error("wrong signature", "err", err)
|
||||
return err
|
||||
}
|
||||
err = s.apService.RemoveFollower(sender)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go s.apService.Accept(act)
|
||||
return nil
|
||||
}
|
||||
if o.Type == vocab.LikeType {
|
||||
return s.apService.RemoveLike(o.ID.String())
|
||||
}
|
||||
slog.Warn("unsupporeted object type for undo", "object", o)
|
||||
return errors.New("unsupporeted object type")
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (s *ActivityPubServer) processLike(r *http.Request, act *vocab.Activity) error {
|
||||
sender := act.Actor.GetID().String()
|
||||
liked := act.Object.GetID().String()
|
||||
err := s.apService.VerifySignature(r, sender)
|
||||
if err != nil {
|
||||
slog.Error("wrong signature", "err", err)
|
||||
return err
|
||||
}
|
||||
err = s.apService.RemoveFollower(follower)
|
||||
|
||||
err = s.apService.AddLike(sender, liked, act.ID.String())
|
||||
if err != nil {
|
||||
slog.Error("error saving like", "err", err)
|
||||
return err
|
||||
}
|
||||
|
||||
go s.apService.Accept(act)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ActivityPubServer) HandleInbox(ctx *fiber.Ctx) error {
|
||||
// siteConfig, _ := s.siteConfigService.GetSiteConfig()
|
||||
// apConfig, _ := s.apService.GetApConfig()
|
||||
|
||||
body := ctx.Request().Body()
|
||||
data, err := vocab.UnmarshalJSON(body)
|
||||
if err != nil {
|
||||
|
@ -198,11 +223,15 @@ func (s *ActivityPubServer) HandleInbox(ctx *fiber.Ctx) error {
|
|||
if act.Type == vocab.FollowType {
|
||||
return s.processFollow(r, act)
|
||||
}
|
||||
|
||||
if act.Type == vocab.UndoType {
|
||||
slog.Info("processing undo")
|
||||
return s.processUndo(r, act)
|
||||
}
|
||||
if act.Type == vocab.LikeType {
|
||||
return s.processLike(r, act)
|
||||
}
|
||||
|
||||
slog.Warn("Unsupported action", "body", body)
|
||||
|
||||
return errors.New("only follow and undo actions supported")
|
||||
})
|
||||
return err
|
||||
|
|
Loading…
Reference in New Issue