From 5e77fdd33b3386a172f65a5c574a5ab07852b11b Mon Sep 17 00:00:00 2001 From: Niko Abeler Date: Wed, 26 Jul 2023 21:28:57 +0200 Subject: [PATCH] WIP minimal AP --- go.mod | 5 ++ go.sum | 11 ++++ web/activity_pub_handler.go | 120 +++++++++++++++++++----------------- web/app.go | 6 +- 4 files changed, 84 insertions(+), 58 deletions(-) diff --git a/go.mod b/go.mod index 902591b..1af7299 100644 --- a/go.mod +++ b/go.mod @@ -15,8 +15,12 @@ require ( ) require ( + git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect github.com/andybalholm/brotli v1.0.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-ap/activitypub v0.0.0-20230719093539-2b6a6f3a25ee // indirect + github.com/go-ap/errors v0.0.0-20221205040414-01c1adfc98ea // indirect + github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/klauspost/compress v1.16.3 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -31,6 +35,7 @@ require ( github.com/tinylib/msgp v1.1.8 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.47.0 // indirect + github.com/valyala/fastjson v1.6.4 // indirect github.com/valyala/tcplisten v1.0.0 // indirect golang.org/x/sys v0.10.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 68ace32..a5a4c5e 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,16 @@ +git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:cliQ4HHsCo6xi2oWZYKWW4bly/Ory9FuTpFPRxj/mAg= +git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-ap/activitypub v0.0.0-20230719093539-2b6a6f3a25ee h1:1OMBlmSzLXftIj5z/D1s1Xr3FanVKtLFZPtdIFslh1A= +github.com/go-ap/activitypub v0.0.0-20230719093539-2b6a6f3a25ee/go.mod h1:qw0WNf+PTG69Xu6mVqUluDuKl1VwVYdgntOZQFBZQ48= +github.com/go-ap/errors v0.0.0-20221205040414-01c1adfc98ea h1:ywGtLGVjJjMrq4mu35Qmu+NtlhlTk/gTayE6Bb4tQZk= +github.com/go-ap/errors v0.0.0-20221205040414-01c1adfc98ea/go.mod h1:SaTNjEEkp0q+w3pUS1ccyEL/lUrHteORlDq/e21mCc8= +github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73 h1:GMKIYXyXPGIp+hYiWOhfqK4A023HdgisDT4YGgf99mw= +github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73/go.mod h1:jyveZeGw5LaADntW+UEsMjl3IlIwk+DxlYNsbofQkGA= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/gofiber/fiber/v2 v2.47.0 h1:EN5lHVCc+Pyqh5OEsk8fzRiifgwpbrP0rulQ4iNf3fs= @@ -53,6 +61,9 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.47.0 h1:y7moDoxYzMooFpT5aHgNgVOQDrS3qlkfiP9mDtGGK9c= github.com/valyala/fasthttp v1.47.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= +github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= +github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= +github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/web/activity_pub_handler.go b/web/activity_pub_handler.go index a322b16..d5d5eec 100644 --- a/web/activity_pub_handler.go +++ b/web/activity_pub_handler.go @@ -1,17 +1,22 @@ package web import ( + "net/url" + "owl-blogs/app" "owl-blogs/app/repository" "owl-blogs/config" "owl-blogs/domain/model" + vocab "github.com/go-ap/activitypub" + "github.com/gofiber/fiber/v2" ) const ACT_PUB_CONF_NAME = "activity_pub" type ActivityPubServer struct { - configRepo repository.ConfigRepository + configRepo repository.ConfigRepository + entryService *app.EntryService } type ActivityPubConfig struct { @@ -21,48 +26,20 @@ type ActivityPubConfig struct { } type WebfingerResponse struct { - Subject string `json:"subject"` - Links []ActivityPubLink `json:"links"` + Subject string `json:"subject"` + Links []WebfingerLink `json:"links"` } -type ActivityPubLink struct { +type WebfingerLink struct { Rel string `json:"rel"` Type string `json:"type"` Href string `json:"href"` } -type ActivityPubActor struct { - Context []string `json:"@context"` - - ID string `json:"id"` - Type string `json:"type"` - PreferredUsername string `json:"preferredUsername"` - Inbox string `json:"inbox"` - Oubox string `json:"outbox"` - Followers string `json:"followers"` - - PublicKey ActivityPubPublicKey `json:"publicKey"` -} - -type ActivityPubPublicKey struct { - ID string `json:"id"` - Owner string `json:"owner"` - PublicKeyPem string `json:"publicKeyPem"` -} - -type ActivityPubOrderedCollection struct { - Context []string `json:"@context"` - - ID string `json:"id"` - Type string `json:"type"` - TotalItems int `json:"totalItems"` - First string `json:"first"` - Last string `json:"last"` -} - -func NewActivityPubServer(configRepo repository.ConfigRepository) *ActivityPubServer { +func NewActivityPubServer(configRepo repository.ConfigRepository, entryService *app.EntryService) *ActivityPubServer { return &ActivityPubServer{ - configRepo: configRepo, + configRepo: configRepo, + entryService: entryService, } } @@ -75,7 +52,7 @@ func (s *ActivityPubServer) HandleWebfinger(ctx *fiber.Ctx) error { webfinger := WebfingerResponse{ Subject: ctx.Query("resource"), - Links: []ActivityPubLink{ + Links: []WebfingerLink{ { Rel: "self", Type: "application/activity+json", @@ -90,6 +67,7 @@ func (s *ActivityPubServer) HandleWebfinger(ctx *fiber.Ctx) error { func (s *ActivityPubServer) Router(router fiber.Router) { router.Get("/actor", s.HandleActor) + router.Get("/outbox", s.HandleOutbox) } func (s *ActivityPubServer) HandleActor(ctx *fiber.Ctx) error { @@ -98,25 +76,57 @@ func (s *ActivityPubServer) HandleActor(ctx *fiber.Ctx) error { s.configRepo.Get(ACT_PUB_CONF_NAME, &apConfig) s.configRepo.Get(config.SITE_CONFIG, &siteConfig) - actor := ActivityPubActor{ - Context: []string{ - "https://www.w3.org/ns/activitystreams", - "https://w3id.org/security/v1", - }, - - ID: siteConfig.FullUrl + "/activitypub/actor", - Type: "Person", - PreferredUsername: apConfig.PreferredUsername, - Inbox: siteConfig.FullUrl + "/activitypub/inbox", - Oubox: siteConfig.FullUrl + "/activitypub/outbox", - Followers: siteConfig.FullUrl + "/activitypub/followers", - - PublicKey: ActivityPubPublicKey{ - ID: siteConfig.FullUrl + "/activitypub/actor#main-key", - Owner: siteConfig.FullUrl + "/activitypub/actor", - PublicKeyPem: apConfig.PublicKeyPem, - }, + actor := vocab.PersonNew(vocab.IRI(siteConfig.FullUrl + "/activitypub/actor")) + actor.PreferredUsername = vocab.NaturalLanguageValues{{Value: vocab.Content(apConfig.PreferredUsername)}} + actor.Inbox = vocab.IRI(siteConfig.FullUrl + "/activitypub/inbox") + actor.Outbox = vocab.IRI(siteConfig.FullUrl + "/activitypub/outbox") + actor.Followers = vocab.IRI(siteConfig.FullUrl + "/activitypub/followers") + actor.PublicKey = vocab.PublicKey{ + ID: vocab.ID(siteConfig.FullUrl + "/activitypub/actor#main-key"), + Owner: vocab.IRI(siteConfig.FullUrl + "/activitypub/actor"), + PublicKeyPem: apConfig.PublicKeyPem, } - return ctx.JSON(actor) + data, err := actor.MarshalJSON() + if err != nil { + return err + } + ctx.Set("Content-Type", "application/activity+json") + return ctx.Send(data) +} + +func (s *ActivityPubServer) HandleOutbox(ctx *fiber.Ctx) error { + siteConfig := model.SiteConfig{} + apConfig := ActivityPubConfig{} + s.configRepo.Get(ACT_PUB_CONF_NAME, &apConfig) + s.configRepo.Get(config.SITE_CONFIG, &siteConfig) + + entries, err := s.entryService.FindAllByType(nil, true, false) + if err != nil { + return err + } + + items := make([]vocab.Item, len(entries)) + for i, entry := range entries { + url, _ := url.JoinPath(siteConfig.FullUrl, "/posts/"+entry.ID()+"/") + items[i] = *vocab.ActivityNew(vocab.IRI(url), vocab.CreateType, vocab.Object{ + ID: vocab.ID(url), + Type: vocab.ArticleType, + Content: vocab.NaturalLanguageValues{ + {Value: vocab.Content(entry.Content())}, + }, + }) + } + + outbox := vocab.OrderedCollectionNew(vocab.IRI(siteConfig.FullUrl + "/activitypub/outbox")) + outbox.TotalItems = uint(len(items)) + outbox.OrderedItems = items + + data, err := outbox.MarshalJSON() + if err != nil { + return err + } + ctx.Set("Content-Type", "application/activity+json") + return ctx.Send(data) + } diff --git a/web/app.go b/web/app.go index b51aed2..7cd03a3 100644 --- a/web/app.go +++ b/web/app.go @@ -118,10 +118,10 @@ func NewWebApp( app.Get("/sitemap.xml", NewSiteMapHandler(entryService, configRepo).Handle) // ActivityPub - // activityPubServer := NewActivityPubServer(configRepo) + activityPubServer := NewActivityPubServer(configRepo, entryService) configRegister.Register(ACT_PUB_CONF_NAME, &ActivityPubConfig{}) - // app.Get("/.well-known/webfinger", activityPubServer.HandleWebfinger) - // app.Route("/activitypub", activityPubServer.Router) + app.Get("/.well-known/webfinger", activityPubServer.HandleWebfinger) + app.Route("/activitypub", activityPubServer.Router) // Webmention // app.Post("/webmention/", userWebmentionHandler(repo))