outbox.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. package controllers
  2. import (
  3. "fmt"
  4. "net/http"
  5. "strconv"
  6. "strings"
  7. "github.com/go-fed/activity/streams"
  8. "github.com/go-fed/activity/streams/vocab"
  9. "github.com/owncast/owncast/activitypub/apmodels"
  10. "github.com/owncast/owncast/activitypub/crypto"
  11. "github.com/owncast/owncast/activitypub/persistence"
  12. "github.com/owncast/owncast/activitypub/requests"
  13. "github.com/pkg/errors"
  14. log "github.com/sirupsen/logrus"
  15. )
  16. const (
  17. outboxPageSize = 50
  18. )
  19. // OutboxHandler will handle requests for the local ActivityPub outbox.
  20. func OutboxHandler(w http.ResponseWriter, r *http.Request) {
  21. if r.Method != http.MethodGet {
  22. w.WriteHeader(http.StatusMethodNotAllowed)
  23. return
  24. }
  25. var response interface{}
  26. var err error
  27. if r.URL.Query().Get("page") != "" {
  28. response, err = getOutboxPage(r.URL.Query().Get("page"), r)
  29. } else {
  30. response, err = getInitialOutboxHandler(r)
  31. }
  32. if response == nil {
  33. w.WriteHeader(http.StatusInternalServerError)
  34. return
  35. }
  36. if err != nil {
  37. _, _ = w.Write([]byte(err.Error()))
  38. w.WriteHeader(http.StatusInternalServerError)
  39. return
  40. }
  41. pathComponents := strings.Split(r.URL.Path, "/")
  42. accountName := pathComponents[3]
  43. actorIRI := apmodels.MakeLocalIRIForAccount(accountName)
  44. publicKey := crypto.GetPublicKey(actorIRI)
  45. if err := requests.WriteStreamResponse(response.(vocab.Type), w, publicKey); err != nil {
  46. log.Errorln("unable to write stream response for outbox handler", err)
  47. }
  48. }
  49. // ActorObjectHandler will handle the request for a single ActivityPub object.
  50. func ActorObjectHandler(w http.ResponseWriter, r *http.Request) {
  51. object, _, _, err := persistence.GetObjectByIRI(r.URL.Path)
  52. if err != nil {
  53. w.WriteHeader(http.StatusNotFound)
  54. return
  55. // controllers.WriteSimpleResponse(w, false, err.Error())
  56. }
  57. if _, err := w.Write([]byte(object)); err != nil {
  58. log.Errorln(err)
  59. }
  60. }
  61. func getInitialOutboxHandler(r *http.Request) (vocab.ActivityStreamsOrderedCollection, error) {
  62. collection := streams.NewActivityStreamsOrderedCollection()
  63. idProperty := streams.NewJSONLDIdProperty()
  64. id, err := createPageURL(r, nil)
  65. if err != nil {
  66. return nil, errors.Wrap(err, "unable to create followers page property")
  67. }
  68. idProperty.SetIRI(id)
  69. collection.SetJSONLDId(idProperty)
  70. totalPosts, err := persistence.GetOutboxPostCount()
  71. if err != nil {
  72. return nil, errors.Wrap(err, "unable to get outbox post count")
  73. }
  74. totalItemsProperty := streams.NewActivityStreamsTotalItemsProperty()
  75. totalItemsProperty.Set(int(totalPosts))
  76. collection.SetActivityStreamsTotalItems(totalItemsProperty)
  77. first := streams.NewActivityStreamsFirstProperty()
  78. page := "1"
  79. firstIRI, err := createPageURL(r, &page)
  80. if err != nil {
  81. return nil, errors.Wrap(err, "unable to create first page property")
  82. }
  83. first.SetIRI(firstIRI)
  84. collection.SetActivityStreamsFirst(first)
  85. return collection, nil
  86. }
  87. func getOutboxPage(page string, r *http.Request) (vocab.ActivityStreamsOrderedCollectionPage, error) {
  88. pageInt, err := strconv.Atoi(page)
  89. if err != nil {
  90. return nil, errors.Wrap(err, "unable to parse page number")
  91. }
  92. postCount, err := persistence.GetOutboxPostCount()
  93. if err != nil {
  94. return nil, errors.Wrap(err, "unable to get outbox post count")
  95. }
  96. collectionPage := streams.NewActivityStreamsOrderedCollectionPage()
  97. idProperty := streams.NewJSONLDIdProperty()
  98. id, err := createPageURL(r, &page)
  99. if err != nil {
  100. return nil, errors.Wrap(err, "unable to create followers page ID")
  101. }
  102. idProperty.SetIRI(id)
  103. collectionPage.SetJSONLDId(idProperty)
  104. orderedItems := streams.NewActivityStreamsOrderedItemsProperty()
  105. outboxItems, err := persistence.GetOutbox(outboxPageSize, (pageInt-1)*outboxPageSize)
  106. if err != nil {
  107. return nil, errors.Wrap(err, "unable to get federation followers")
  108. }
  109. orderedItems.AppendActivityStreamsOrderedCollection(outboxItems)
  110. collectionPage.SetActivityStreamsOrderedItems(orderedItems)
  111. partOf := streams.NewActivityStreamsPartOfProperty()
  112. partOfIRI, err := createPageURL(r, nil)
  113. if err != nil {
  114. return nil, errors.Wrap(err, "unable to create partOf property for outbox page")
  115. }
  116. partOf.SetIRI(partOfIRI)
  117. collectionPage.SetActivityStreamsPartOf(partOf)
  118. if pageInt*followersPageSize < int(postCount) {
  119. next := streams.NewActivityStreamsNextProperty()
  120. nextPage := fmt.Sprintf("%d", pageInt+1)
  121. nextIRI, err := createPageURL(r, &nextPage)
  122. if err != nil {
  123. return nil, errors.Wrap(err, "unable to create next page property")
  124. }
  125. next.SetIRI(nextIRI)
  126. collectionPage.SetActivityStreamsNext(next)
  127. }
  128. return collectionPage, nil
  129. }