mirror of https://github.com/jlelse/GoBlog
Publish ActivityPub followers collection
This commit is contained in:
parent
223ec95487
commit
0df2802c74
|
@ -208,22 +208,7 @@ func (a *goBlog) apHandleInbox(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
case ap.CreateType, ap.UpdateType:
|
||||
if activity.Object.IsObject() {
|
||||
if object, err := ap.ToObject(activity.Object); err == nil &&
|
||||
(object.GetType() == ap.NoteType || object.GetType() == ap.ArticleType) &&
|
||||
(object.To.Contains(ap.PublicNS) || object.CC.Contains(ap.PublicNS)) {
|
||||
target := object.InReplyTo.GetID().String()
|
||||
original := object.GetID().String()
|
||||
name := requestActor.Name.First().Value.String()
|
||||
if username := requestActor.PreferredUsername.First().String(); name == "" && username != "" {
|
||||
name = username
|
||||
}
|
||||
website := requestActor.GetLink().String()
|
||||
if actorUrl := requestActor.URL.GetLink(); actorUrl != "" {
|
||||
website = actorUrl.String()
|
||||
}
|
||||
content := object.Content.First().Value.String()
|
||||
_, _, _ = a.createComment(blog, target, content, name, website, original)
|
||||
}
|
||||
a.apOnCreateUpdate(blog, requestActor, activity)
|
||||
}
|
||||
case ap.DeleteType, ap.BlockType:
|
||||
if activity.Object.GetID() == activityActor {
|
||||
|
@ -245,6 +230,38 @@ func (a *goBlog) apHandleInbox(w http.ResponseWriter, r *http.Request) {
|
|||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func (a *goBlog) apOnCreateUpdate(blog *configBlog, requestActor *ap.Actor, activity *ap.Activity) {
|
||||
object, err := ap.ToObject(activity.Object)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if object.GetType() != ap.NoteType && object.GetType() != ap.ArticleType {
|
||||
// ignore other objects for now
|
||||
return
|
||||
}
|
||||
visible := true
|
||||
if !object.To.Contains(ap.PublicNS) && !object.CC.Contains(ap.PublicNS) {
|
||||
visible = false
|
||||
}
|
||||
if replyTarget := object.InReplyTo.GetID().String(); visible && replyTarget != "" && strings.HasPrefix(replyTarget, a.cfg.Server.PublicAddress) {
|
||||
// It's a reply
|
||||
original := object.GetID().String()
|
||||
name := requestActor.Name.First().Value.String()
|
||||
if username := requestActor.PreferredUsername.First().String(); name == "" && username != "" {
|
||||
name = username
|
||||
}
|
||||
website := requestActor.GetLink().String()
|
||||
if actorUrl := requestActor.URL.GetLink(); actorUrl != "" {
|
||||
website = actorUrl.String()
|
||||
}
|
||||
content := object.Content.First().Value.String()
|
||||
_, _, _ = a.createComment(blog, replyTarget, content, name, website, original)
|
||||
return
|
||||
}
|
||||
// Might be a private reply or mention etc.
|
||||
// TODO: handle them
|
||||
}
|
||||
|
||||
func (a *goBlog) apVerifySignature(r *http.Request, blogIri string) (*ap.Actor, string, int, error) {
|
||||
verifier, err := httpsig.NewVerifier(r)
|
||||
if err != nil {
|
||||
|
@ -278,6 +295,40 @@ func handleWellKnownHostMeta(w http.ResponseWriter, r *http.Request) {
|
|||
_, _ = io.WriteString(w, `<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><Link rel="lrdd" type="application/xrd+xml" template="https://`+r.Host+`/.well-known/webfinger?resource={uri}"/></XRD>`)
|
||||
}
|
||||
|
||||
func (a *goBlog) apGetFollowersCollectionId(blogName string, blog *configBlog) ap.IRI {
|
||||
return ap.IRI(a.apIri(blog) + "/activitypub/followers/" + blogName)
|
||||
}
|
||||
|
||||
func (a *goBlog) apShowFollowers(w http.ResponseWriter, r *http.Request) {
|
||||
blogName := chi.URLParam(r, "blog")
|
||||
blog, ok := a.cfg.Blogs[blogName]
|
||||
if !ok || blog == nil {
|
||||
a.serveError(w, r, "Blog not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
followers, err := a.db.apGetAllFollowers(blogName)
|
||||
if err != nil {
|
||||
a.serveError(w, r, "Failed to get followers", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if asRequest, ok := r.Context().Value(asRequestKey).(bool); ok && asRequest {
|
||||
followersCollection := ap.CollectionNew(a.apGetFollowersCollectionId(blogName, blog))
|
||||
for _, follower := range followers {
|
||||
followersCollection.Items.Append(ap.IRI(follower.follower))
|
||||
}
|
||||
followersCollection.TotalItems = uint(len(followers))
|
||||
a.serveAPItem(followersCollection, w, r)
|
||||
return
|
||||
}
|
||||
a.render(w, r, a.renderActivityPubFollowers, &renderData{
|
||||
BlogString: blogName,
|
||||
Data: &activityPubFollowersRenderData{
|
||||
apUser: fmt.Sprintf("@%s@%s", blogName, a.cfg.Server.publicHostname),
|
||||
followers: followers,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (a *goBlog) apGetRemoteActor(iri, ownBlogIri string) (*ap.Actor, int, error) {
|
||||
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, iri, strings.NewReader(""))
|
||||
if err != nil {
|
||||
|
|
|
@ -12,26 +12,6 @@ import (
|
|||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
func (a *goBlog) apShowFollowers(w http.ResponseWriter, r *http.Request) {
|
||||
blogName := chi.URLParam(r, "blog")
|
||||
blog, ok := a.cfg.Blogs[blogName]
|
||||
if !ok || blog == nil {
|
||||
a.serveError(w, r, "Blog not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
followers, err := a.db.apGetAllFollowers(blogName)
|
||||
if err != nil {
|
||||
a.serveError(w, r, "Failed to get followers", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
a.render(w, r, a.renderActivityPubFollowers, &renderData{
|
||||
BlogString: blogName,
|
||||
Data: &activityPubFollowersRenderData{
|
||||
followers: followers,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (a *goBlog) apRemoteFollow(w http.ResponseWriter, r *http.Request) {
|
||||
blogName := chi.URLParam(r, "blog")
|
||||
blog, ok := a.cfg.Blogs[blogName]
|
||||
|
|
|
@ -37,16 +37,7 @@ func (a *goBlog) checkActivityStreamsRequest(next http.Handler) http.Handler {
|
|||
}
|
||||
|
||||
func (a *goBlog) serveActivityStreamsPost(p *post, w http.ResponseWriter, r *http.Request) {
|
||||
note := a.toAPNote(p)
|
||||
// Encode
|
||||
binary, err := jsonld.WithContext(jsonld.IRI(ap.ActivityBaseURI), jsonld.IRI(ap.SecurityContextURI)).Marshal(note)
|
||||
if err != nil {
|
||||
a.serveError(w, r, "Encoding failed", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
// Send response
|
||||
w.Header().Set(contentType, contenttype.ASUTF8)
|
||||
_ = a.min.Get().Minify(contenttype.AS, w, bytes.NewReader(binary))
|
||||
a.serveAPItem(a.toAPNote(p), w, r)
|
||||
}
|
||||
|
||||
func (a *goBlog) toAPNote(p *post) *ap.Note {
|
||||
|
@ -124,6 +115,8 @@ func (a *goBlog) toApPerson(blog string) *ap.Person {
|
|||
apBlog.PreferredUsername.Set(ap.DefaultLang, ap.Content(blog))
|
||||
|
||||
apBlog.Inbox = ap.IRI(a.getFullAddress("/activitypub/inbox/" + blog))
|
||||
apBlog.Followers = ap.IRI(a.getFullAddress("/activitypub/followers/" + blog))
|
||||
|
||||
apBlog.PublicKey.Owner = apIri
|
||||
apBlog.PublicKey.ID = ap.IRI(a.apIri(b) + "#main-key")
|
||||
apBlog.PublicKey.PublicKeyPem = string(pem.EncodeToMemory(&pem.Block{
|
||||
|
@ -144,9 +137,12 @@ func (a *goBlog) toApPerson(blog string) *ap.Person {
|
|||
}
|
||||
|
||||
func (a *goBlog) serveActivityStreams(blog string, w http.ResponseWriter, r *http.Request) {
|
||||
person := a.toApPerson(blog)
|
||||
a.serveAPItem(a.toApPerson(blog), w, r)
|
||||
}
|
||||
|
||||
func (a *goBlog) serveAPItem(item any, w http.ResponseWriter, r *http.Request) {
|
||||
// Encode
|
||||
binary, err := jsonld.WithContext(jsonld.IRI(ap.ActivityBaseURI), jsonld.IRI(ap.SecurityContextURI)).Marshal(person)
|
||||
binary, err := jsonld.WithContext(jsonld.IRI(ap.ActivityBaseURI), jsonld.IRI(ap.SecurityContextURI)).Marshal(item)
|
||||
if err != nil {
|
||||
a.serveError(w, r, "Encoding failed", http.StatusInternalServerError)
|
||||
return
|
||||
|
|
|
@ -42,9 +42,7 @@ func (a *goBlog) activityPubRouter(r chi.Router) {
|
|||
if ap := a.cfg.ActivityPub; ap != nil && ap.Enabled {
|
||||
r.Route("/activitypub", func(r chi.Router) {
|
||||
r.Post("/inbox/{blog}", a.apHandleInbox)
|
||||
r.Post("/{blog}/inbox", a.apHandleInbox) // old
|
||||
r.With(a.authMiddleware).Get("/{blog}/followers", a.apShowFollowers)
|
||||
r.With(a.authMiddleware).Get("/followers/{blog}", a.apShowFollowers) // old
|
||||
r.With(a.checkActivityStreamsRequest).Get("/followers/{blog}", a.apShowFollowers)
|
||||
r.With(a.cacheMiddleware).Get("/remote_follow/{blog}", a.apRemoteFollow)
|
||||
r.Post("/remote_follow/{blog}", a.apRemoteFollow)
|
||||
})
|
||||
|
|
3
ui.go
3
ui.go
|
@ -1583,6 +1583,7 @@ func (a *goBlog) renderSettings(hb *htmlbuilder.HtmlBuilder, rd *renderData) {
|
|||
}
|
||||
|
||||
type activityPubFollowersRenderData struct {
|
||||
apUser string
|
||||
followers []*apFollower
|
||||
}
|
||||
|
||||
|
@ -1603,7 +1604,7 @@ func (a *goBlog) renderActivityPubFollowers(hb *htmlbuilder.HtmlBuilder, rd *ren
|
|||
hb.WriteElementOpen("h1")
|
||||
hb.WriteEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "apfollowers"))
|
||||
hb.WriteEscaped(": ")
|
||||
hb.WriteEscaped(rd.BlogString)
|
||||
hb.WriteEscaped(aprd.apUser)
|
||||
hb.WriteElementClose("h1")
|
||||
|
||||
// List followers
|
||||
|
|
Loading…
Reference in New Issue