From b3f22c698739bdd57dab77af45a0b8a43da72ca4 Mon Sep 17 00:00:00 2001 From: Julio Capote Date: Sun, 1 Jan 2023 16:48:37 -0500 Subject: working ids and activity lookups --- http/server.go | 31 +++++++++++++++---------------- models/model.go | 1 + models/outbox_item.go | 11 +++++------ models/persister.go | 26 ++++++++++++++++++++++++++ registry/registry.go | 22 ++++++++++++++++++++++ urls/urls.go | 17 +++++++++++++++++ views/activity.go | 20 ++++++++++++++++++++ views/outbox.go | 17 +++++++++++++++++ 8 files changed, 123 insertions(+), 22 deletions(-) create mode 100644 views/activity.go diff --git a/http/server.go b/http/server.go index 98b4083..7d18085 100644 --- a/http/server.go +++ b/http/server.go @@ -8,6 +8,7 @@ import ( "github.com/gin-gonic/gin" ) +// TODO rename to Router and router.go type Server struct { registry *registry.Registry } @@ -76,22 +77,20 @@ func (s *Server) Start(zapWriter io.Writer) { } }) - // // outbox single - // router.GET("/actors/:actor/outbox/:id", func(c *gin.Context) { - // actorParam := c.Param("actor") - // var resource map[string]interface{} - // if c.Query("page") == "true" { - // resource, _ = s.registry.OutboxPage(actorParam) - // } else { - // resource, _ = s.registry.Outbox(actorParam) - // } - // if resource != nil { - // c.Writer.Header().Set("Content-Type", "application/activity+json") - // c.JSON(http.StatusOK, resource) - // } else { - // c.JSON(http.StatusNotFound, nil) - // } - // }) + // Single activity + router.GET("/actors/:actor/outbox/:id/activity", func(c *gin.Context) { + actorParam := c.Param("actor") + idParam := c.Param("id") + var resource map[string]interface{} + resource, _ = s.registry.Activity(actorParam, idParam) + + if resource != nil { + c.Writer.Header().Set("Content-Type", "application/activity+json") + c.JSON(http.StatusOK, resource) + } else { + c.JSON(http.StatusNotFound, nil) + } + }) router.Run() } diff --git a/models/model.go b/models/model.go index 56f1680..354b3bd 100644 --- a/models/model.go +++ b/models/model.go @@ -5,4 +5,5 @@ import "github.com/dgraph-io/badger/v3" type model interface { Save(txn *badger.Txn) error Keybase() string + Key() string } diff --git a/models/outbox_item.go b/models/outbox_item.go index 9950d9c..9d9de97 100644 --- a/models/outbox_item.go +++ b/models/outbox_item.go @@ -18,7 +18,7 @@ type OutboxItem struct { CreatedAt time.Time } -// used for lookup purposes (count, collect) +// used for lookup purposes (count, collect, find) func NewOutboxItem(h config.Handler) *OutboxItem { aso := &OutboxItem{Handler: h} return aso @@ -31,14 +31,13 @@ func CreateOutboxItem(h config.Handler, content []byte) *OutboxItem { Handler: h, CreatedAt: t, Content: content, - Id: k.Bytes(), + Id: []byte(k.String()), // NOTE: we want the bytes of the string representation of a hash, NOT a binary hash } return aso } -func (a *OutboxItem) keyName() []byte { - key := fmt.Sprintf("%s:%s", a.Keybase(), a.Id) - return []byte(key) +func (a *OutboxItem) Key() string { + return fmt.Sprintf("%s:%s", a.Keybase(), a.Id) } func (a *OutboxItem) Keybase() string { @@ -59,6 +58,6 @@ func (a *OutboxItem) Save(txn *badger.Txn) error { if err != nil { return fmt.Errorf("could not encode outbox item: %w", err) } - e := badger.NewEntry(a.keyName(), network.Bytes()) + e := badger.NewEntry([]byte(a.Key()), network.Bytes()) return txn.SetEntry(e) } diff --git a/models/persister.go b/models/persister.go index 669480e..69e79f4 100644 --- a/models/persister.go +++ b/models/persister.go @@ -60,3 +60,29 @@ func (p *Persister) Collect(model model) ([][]byte, error) { }) return result, err } + +func (p *Persister) Find(model model) ([]byte, error) { + var result []byte + var item *badger.Item + err := p.db.View(func(txn *badger.Txn) error { + var getErr error + item, getErr = txn.Get([]byte(model.Key())) + if getErr != nil { + return getErr + } + return nil + }) + if err != nil { + return nil, err + } + err = item.Value(func(v []byte) error { + result = v + return nil + }) + + if err != nil { + return nil, err + } + + return result, nil +} diff --git a/registry/registry.go b/registry/registry.go index 2518d55..4a1329a 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -14,6 +14,7 @@ type Handler struct { handlerCfg config.Handler } +// TODO rename to controller and controller.go type Registry struct { cfg config.Config persister *models.Persister @@ -74,6 +75,27 @@ func (r *Registry) OutboxCollection(name string) (map[string]interface{}, error) return views.RenderOutboxCollection(handler.handlerCfg.Name, r.cfg.Domain, outboxItems) } +func (r *Registry) Activity(name, id string) (map[string]interface{}, error) { + handler := r.findByName(name) + if handler == nil { + return nil, nil + } + lookup := models.NewOutboxItem(handler.handlerCfg) + lookup.Id = []byte(id) + result, err := r.persister.Find(lookup) + if err != nil { + return nil, err + } + buf := bytes.NewBuffer(result) + dec := gob.NewDecoder(buf) + var outboxItem models.OutboxItem + err = dec.Decode(&outboxItem) + if err != nil { + return nil, err + } + return views.RenderActivity(handler.handlerCfg.Name, r.cfg.Domain, outboxItem) +} + func (r *Registry) WebfingerResource(fqn string) (*views.WebfingerResource, error) { handler := r.findByFQN(fqn) if handler == nil { diff --git a/urls/urls.go b/urls/urls.go index 5f563a7..3c08e07 100644 --- a/urls/urls.go +++ b/urls/urls.go @@ -22,6 +22,23 @@ func UrlOutbox(name, domain string) (*url.URL, error) { return u, nil } +func UrlActivity(name, domain, id string) (*url.URL, error) { + u, err := url.Parse(path.Join(domain, "actors", name, "outbox", id, "activity")) + if err != nil { + panic(err) + return nil, fmt.Errorf("could not build activity url: %w", err) + } + return u, nil +} + +func UrlNote(name, domain, id string) (*url.URL, error) { + u, err := url.Parse(path.Join("https://", domain, "actors", name, "outbox", id, "note")) + if err != nil { + return nil, fmt.Errorf("could not build note url: %w", err) + } + return u, nil +} + func UrlOutboxPage(name, domain string) (*url.URL, error) { u, err := UrlOutbox(name, domain) if err != nil { diff --git a/views/activity.go b/views/activity.go new file mode 100644 index 0000000..cd34b6c --- /dev/null +++ b/views/activity.go @@ -0,0 +1,20 @@ +package views + +import ( + "net/url" + + "git.capotej.com/capotej/communique/models" + "github.com/go-fed/activity/streams" +) + +func RenderActivity(name, domain string, item models.OutboxItem) (map[string]interface{}, error) { + publicUrl, err := url.Parse("https://www.w3.org/ns/activitystreams#Public") + if err != nil { + return nil, err + } + toProp := streams.NewActivityStreamsToProperty() + toProp.AppendIRI(publicUrl) + crea := streams.NewActivityStreamsCreate() + crea.SetActivityStreamsTo(toProp) + return streams.Serialize(crea) +} diff --git a/views/outbox.go b/views/outbox.go index bfe225a..ee28842 100644 --- a/views/outbox.go +++ b/views/outbox.go @@ -44,11 +44,28 @@ func RenderOutboxCollection(name, domain string, page []models.OutboxItem) (map[ publishedProp.Set(v.CreatedAt) crea := streams.NewActivityStreamsCreate() obj := streams.NewActivityStreamsObjectProperty() + + creaIdProp := streams.NewJSONLDIdProperty() + activityUrl, err := urls.UrlActivity(name, domain, string(v.Id)) + if err != nil { + return nil, err + } + creaIdProp.Set(activityUrl) + crea.SetJSONLDId(creaIdProp) + crea.SetActivityStreamsObject(obj) crea.SetActivityStreamsTo(toProp) crea.SetActivityStreamsPublished(publishedProp) + noteUrl, err := urls.UrlNote(name, domain, string(v.Id)) + if err != nil { + return nil, err + } + note := streams.NewActivityStreamsNote() + noteIdProp := streams.NewJSONLDIdProperty() + noteIdProp.Set(noteUrl) + note.SetJSONLDId(noteIdProp) contentProp := streams.NewActivityStreamsContentProperty() contentProp.AppendXMLSchemaString(string(v.Content)) note.SetActivityStreamsContent(contentProp) -- cgit v1.2.3