From 68e8f842207c000b80416f6af70990a7e5cf2001 Mon Sep 17 00:00:00 2001 From: Niko Abeler Date: Sat, 22 Jul 2023 20:34:17 +0200 Subject: [PATCH] WIP activity pub PoC --- app/config_register.go | 13 ++++ app/entry_creation_bus.go | 25 ++++++++ render/templates/base.tmpl | 2 +- web/activity_pub_handler.go | 122 ++++++++++++++++++++++++++++++++++++ web/app.go | 6 ++ 5 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 app/config_register.go create mode 100644 app/entry_creation_bus.go create mode 100644 web/activity_pub_handler.go diff --git a/app/config_register.go b/app/config_register.go new file mode 100644 index 0000000..cc34104 --- /dev/null +++ b/app/config_register.go @@ -0,0 +1,13 @@ +package app + +type ConfigRegister struct { + configs map[string]interface{} +} + +func NewConfigRegister() *ConfigRegister { + return &ConfigRegister{configs: map[string]interface{}{}} +} + +func (r *ConfigRegister) Register(name string, config interface{}) { + r.configs[name] = config +} diff --git a/app/entry_creation_bus.go b/app/entry_creation_bus.go new file mode 100644 index 0000000..ffb31b7 --- /dev/null +++ b/app/entry_creation_bus.go @@ -0,0 +1,25 @@ +package app + +import "owl-blogs/domain/model" + +type EntryCreationSubscriber interface { + NotifyEntryCreation(entry model.Entry) +} + +type EntryCreationBus struct { + subscribers []EntryCreationSubscriber +} + +func NewEntryCreationBus() *EntryCreationBus { + return &EntryCreationBus{subscribers: make([]EntryCreationSubscriber, 0)} +} + +func (b *EntryCreationBus) Subscribe(subscriber EntryCreationSubscriber) { + b.subscribers = append(b.subscribers, subscriber) +} + +func (b *EntryCreationBus) Notify(entry model.Entry) { + for _, subscriber := range b.subscribers { + subscriber.NotifyEntryCreation(entry) + } +} diff --git a/render/templates/base.tmpl b/render/templates/base.tmpl index e85f769..213149d 100644 --- a/render/templates/base.tmpl +++ b/render/templates/base.tmpl @@ -1,6 +1,6 @@ {{define "base"}} - + diff --git a/web/activity_pub_handler.go b/web/activity_pub_handler.go new file mode 100644 index 0000000..a322b16 --- /dev/null +++ b/web/activity_pub_handler.go @@ -0,0 +1,122 @@ +package web + +import ( + "owl-blogs/app/repository" + "owl-blogs/config" + "owl-blogs/domain/model" + + "github.com/gofiber/fiber/v2" +) + +const ACT_PUB_CONF_NAME = "activity_pub" + +type ActivityPubServer struct { + configRepo repository.ConfigRepository +} + +type ActivityPubConfig struct { + PreferredUsername string `owl:"inputType=text"` + PublicKeyPem string `owl:"inputType=text widget=textarea"` + PrivateKeyPem string `owl:"inputType=text widget=textarea"` +} + +type WebfingerResponse struct { + Subject string `json:"subject"` + Links []ActivityPubLink `json:"links"` +} + +type ActivityPubLink 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 { + return &ActivityPubServer{ + configRepo: configRepo, + } +} + +func (s *ActivityPubServer) HandleWebfinger(ctx *fiber.Ctx) error { + siteConfig := model.SiteConfig{} + apConfig := ActivityPubConfig{} + s.configRepo.Get(ACT_PUB_CONF_NAME, &apConfig) + s.configRepo.Get(config.SITE_CONFIG, &siteConfig) + + webfinger := WebfingerResponse{ + Subject: ctx.Query("resource"), + + Links: []ActivityPubLink{ + { + Rel: "self", + Type: "application/activity+json", + Href: siteConfig.FullUrl + "/activitypub/actor", + }, + }, + } + + return ctx.JSON(webfinger) + +} + +func (s *ActivityPubServer) Router(router fiber.Router) { + router.Get("/actor", s.HandleActor) +} + +func (s *ActivityPubServer) HandleActor(ctx *fiber.Ctx) error { + siteConfig := model.SiteConfig{} + apConfig := ActivityPubConfig{} + 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, + }, + } + + return ctx.JSON(actor) +} diff --git a/web/app.go b/web/app.go index 79859c8..0d5cfbf 100644 --- a/web/app.go +++ b/web/app.go @@ -89,6 +89,12 @@ func NewWebApp( app.Get("/index.xml", rssHandler.Handle) // Posts app.Get("/posts/:post/", entryHandler.Handle) + + // ActivityPub + // activityPubServer := NewActivityPubServer(configRepo) + // app.Get("/.well-known/webfinger", activityPubServer.HandleWebfinger) + // app.Route("/activitypub", activityPubServer.Router) + // Webmention // app.Post("/webmention/", userWebmentionHandler(repo)) // Micropub