Commit 2fb8ead5 authored by Lukas Matt's avatar Lukas Matt

Merge branch 'activity_pub' into 'master'

Add ActivityPub API calls

See merge request !4
parents f669924c 09ec5aad
This diff is collapsed.
...@@ -22,7 +22,6 @@ import ( ...@@ -22,7 +22,6 @@ import (
"git.feneas.org/ganggo/api/app/helpers" "git.feneas.org/ganggo/api/app/helpers"
"git.feneas.org/ganggo/ganggo/app/jobs" "git.feneas.org/ganggo/ganggo/app/jobs"
run "github.com/revel/modules/jobs/app/jobs" run "github.com/revel/modules/jobs/app/jobs"
federation "git.feneas.org/ganggo/federation"
"git.feneas.org/ganggo/ganggo/app/models" "git.feneas.org/ganggo/ganggo/app/models"
"net/http" "net/http"
) )
...@@ -173,12 +172,7 @@ func (a ApiAspect) CreatePerson() revel.Result { ...@@ -173,12 +172,7 @@ func (a ApiAspect) CreatePerson() revel.Result {
if len(aspects) == 0 { if len(aspects) == 0 {
run.Now(jobs.Dispatcher{ run.Now(jobs.Dispatcher{
User: a.CurrentUser, User: a.CurrentUser,
Message: federation.EntityContact{ Message: membership,
Author: a.CurrentUser.Person.Author,
Recipient: person.Author,
Sharing: true,
Following: true,
},
}) })
} }
...@@ -269,12 +263,7 @@ func (a ApiAspect) DeletePerson() revel.Result { ...@@ -269,12 +263,7 @@ func (a ApiAspect) DeletePerson() revel.Result {
if len(aspects) == 0 { if len(aspects) == 0 {
run.Now(jobs.Dispatcher{ run.Now(jobs.Dispatcher{
User: a.CurrentUser, User: a.CurrentUser,
Message: federation.EntityContact{ Message: membership,
Author: a.CurrentUser.Person.Author,
Recipient: person.Author,
Sharing: false,
Following: false,
},
}) })
} }
return a.RenderJSON(membership) return a.RenderJSON(membership)
......
...@@ -48,6 +48,13 @@ type ApiError struct { ...@@ -48,6 +48,13 @@ type ApiError struct {
Error string `json:"error"` Error string `json:"error"`
} }
func (c ApiHelper) RenderJSONLD(v interface{}) revel.Result {
c.Response.ContentType = "application/ld+json"
c.Response.Out.Header().Add(
"Accept", `application/ld+json; profile="https://www.w3.org/ns/activitystreams"`)
return c.RenderJSON(v)
}
func (c ApiHelper) CatchAll() revel.Result { func (c ApiHelper) CatchAll() revel.Result {
return c.ApiError(http.StatusNotImplemented, ERR_NOT_IMPLEMENTED) return c.ApiError(http.StatusNotImplemented, ERR_NOT_IMPLEMENTED)
} }
......
...@@ -24,7 +24,7 @@ import ( ...@@ -24,7 +24,7 @@ import (
"git.feneas.org/ganggo/api/app/helpers" "git.feneas.org/ganggo/api/app/helpers"
// XXX only for uuid // XXX only for uuid
uuid "git.feneas.org/ganggo/ganggo/app/helpers" uuid "git.feneas.org/ganggo/ganggo/app/helpers"
federation "git.feneas.org/ganggo/federation" run "github.com/revel/modules/jobs/app/jobs"
"strconv" "strconv"
"net/http" "net/http"
) )
...@@ -236,32 +236,24 @@ func (c ApiComment) CreatePost() revel.Result { ...@@ -236,32 +236,24 @@ func (c ApiComment) CreatePost() revel.Result {
return c.ApiError(http.StatusInternalServerError, ERR_SERVER) return c.ApiError(http.StatusInternalServerError, ERR_SERVER)
} }
entityComment := federation.EntityComment{ comment := models.Comment{
Author: c.CurrentUser.Person.Author,
Guid: guid, Guid: guid,
ParentGuid: post.Guid,
Text: text, Text: text,
ShareableID: post.ID,
ShareableType: models.ShareablePost,
PersonID: c.CurrentUser.Person.ID,
} }
var comment models.Comment err = comment.Create()
err = comment.Create(&entityComment)
if err != nil { if err != nil {
c.Log.Error(TAG, "db", err, "api", ERR_SERVER) c.Log.Error(TAG, "db", err, "api", ERR_SERVER)
return c.ApiError(http.StatusInternalServerError, ERR_SERVER) return c.ApiError(http.StatusInternalServerError, ERR_SERVER)
} }
entityComment.CreatedAt.New(comment.CreatedAt)
dispatcher := jobs.Dispatcher{
User: c.CurrentUser,
Model: comment,
Message: entityComment,
}
// XXX run.Now(jobs.Dispatcher{User: c.CurrentUser, Message: comment})
// notify local user about a new comment // XXX
//go like.TriggerNotification(parentUser) // notify local user about a new comment
// deliver to the network //go like.TriggerNotification(parentUser)
go dispatcher.Run()
return c.RenderJSON(comment) return c.RenderJSON(comment)
} }
...@@ -313,12 +305,8 @@ func (c ApiComment) Delete(guid string) revel.Result { ...@@ -313,12 +305,8 @@ func (c ApiComment) Delete(guid string) revel.Result {
} }
if comment.Person.ID == c.CurrentUser.Person.ID { if comment.Person.ID == c.CurrentUser.Person.ID {
entity := federation.EntityRetraction{ dispatcher := jobs.Dispatcher{
Author: c.CurrentUser.Person.Author, User: c.CurrentUser, Retract: true, Message: comment}
TargetGuid: comment.Guid,
TargetType: models.ShareableComment,
}
dispatcher := jobs.Dispatcher{Message: entity}
// NOTE relay to other hosts if we own this entity // NOTE relay to other hosts if we own this entity
// should be done before we start deleting db records // should be done before we start deleting db records
// thats why this call is synchron XXX we should // thats why this call is synchron XXX we should
......
...@@ -22,7 +22,7 @@ import ( ...@@ -22,7 +22,7 @@ import (
"git.feneas.org/ganggo/ganggo/app/models" "git.feneas.org/ganggo/ganggo/app/models"
"git.feneas.org/ganggo/ganggo/app/jobs" "git.feneas.org/ganggo/ganggo/app/jobs"
"git.feneas.org/ganggo/ganggo/app/helpers" "git.feneas.org/ganggo/ganggo/app/helpers"
federation "git.feneas.org/ganggo/federation" run "github.com/revel/modules/jobs/app/jobs"
"net/http" "net/http"
"strconv" "strconv"
) )
...@@ -163,7 +163,6 @@ func (l ApiLike) Create() revel.Result { ...@@ -163,7 +163,6 @@ func (l ApiLike) Create() revel.Result {
var ( var (
postID uint postID uint
post models.Post post models.Post
like models.Like
positive bool positive bool
) )
...@@ -182,31 +181,24 @@ func (l ApiLike) Create() revel.Result { ...@@ -182,31 +181,24 @@ func (l ApiLike) Create() revel.Result {
return l.ApiError(http.StatusInternalServerError, ERR_SERVER) return l.ApiError(http.StatusInternalServerError, ERR_SERVER)
} }
entityLike := federation.EntityLike{ like := models.Like{
Positive: positive, Positive: positive,
ShareableID: post.ID,
PersonID: l.CurrentUser.Person.ID,
Guid: guid, Guid: guid,
Author: l.CurrentUser.Person.Author, ShareableType: models.ShareablePost,
ParentType: models.ShareablePost,
ParentGuid: post.Guid,
} }
err = like.Create(&entityLike) err = like.Create()
if err != nil { if err != nil {
l.Log.Error(TAG, "db", err, "api", ERR_SERVER) l.Log.Error(TAG, "db", err, "api", ERR_SERVER)
return l.ApiError(http.StatusInternalServerError, ERR_SERVER) return l.ApiError(http.StatusInternalServerError, ERR_SERVER)
} }
dispatcher := jobs.Dispatcher{ run.Now(jobs.Dispatcher{User: l.CurrentUser, Message: like})
User: l.CurrentUser, // XXX
Model: like, // notify local user about a new comment
Message: entityLike, //go like.TriggerNotification(parentUser)
}
// XXX
// notify local user about a new comment
//go like.TriggerNotification(parentUser)
// deliver to the network
go dispatcher.Run()
return l.RenderJSON(like) return l.RenderJSON(like)
} }
...@@ -265,12 +257,8 @@ func (l ApiLike) Delete(guid string) revel.Result { ...@@ -265,12 +257,8 @@ func (l ApiLike) Delete(guid string) revel.Result {
} }
if like.PersonID == l.CurrentUser.Person.ID { if like.PersonID == l.CurrentUser.Person.ID {
entity := federation.EntityRetraction{ dispatcher := jobs.Dispatcher{
Author: l.CurrentUser.Person.Author, User: l.CurrentUser, Retract: true, Message: like}
TargetGuid: like.Guid,
TargetType: models.ShareableLike,
}
dispatcher := jobs.Dispatcher{Message: entity}
// NOTE relay to other hosts if we own this entity // NOTE relay to other hosts if we own this entity
// should be done before we start deleting db records // should be done before we start deleting db records
// thats why this call is synchron XXX we should // thats why this call is synchron XXX we should
......
...@@ -25,7 +25,7 @@ import ( ...@@ -25,7 +25,7 @@ import (
"git.feneas.org/ganggo/ganggo/app/helpers" "git.feneas.org/ganggo/ganggo/app/helpers"
"git.feneas.org/ganggo/ganggo/app/models" "git.feneas.org/ganggo/ganggo/app/models"
"git.feneas.org/ganggo/ganggo/app/jobs" "git.feneas.org/ganggo/ganggo/app/jobs"
federation "git.feneas.org/ganggo/federation" run "github.com/revel/modules/jobs/app/jobs"
"net/http" "net/http"
) )
...@@ -170,7 +170,6 @@ func (p ApiPost) Index() revel.Result { ...@@ -170,7 +170,6 @@ func (p ApiPost) Index() revel.Result {
*/ */
func (p ApiPost) Create() revel.Result { func (p ApiPost) Create() revel.Result {
var ( var (
post models.Post
postText, fields string postText, fields string
aspectID uint aspectID uint
) )
...@@ -191,27 +190,21 @@ func (p ApiPost) Create() revel.Result { ...@@ -191,27 +190,21 @@ func (p ApiPost) Create() revel.Result {
return p.ApiError(http.StatusInternalServerError, ERR_SERVER) return p.ApiError(http.StatusInternalServerError, ERR_SERVER)
} }
entity := federation.EntityStatusMessage{ post := models.Post{
Text: postText, CreatedAt: time.Now(),
Author: p.CurrentUser.Person.Author, Public: aspectID <= 0,
Guid: guid, Guid: guid,
ProviderName: "GangGo", Type: models.StatusMessage,
Public: true, Text: postText,
} PersonID: p.CurrentUser.Person.ID,
entity.CreatedAt.New(time.Now())
// this one is a private message
if aspectID > 0 {
entity.Public = false
} }
err = post.Create()
// save post locally
err = post.Create(&entity)
if err != nil { if err != nil {
p.Log.Error(TAG, "db", err, "api", ERR_SERVER) p.Log.Error(TAG, "db", err, "api", ERR_SERVER)
return p.ApiError(http.StatusInternalServerError, ERR_SERVER) return p.ApiError(http.StatusInternalServerError, ERR_SERVER)
} }
if entity.Public == false { if !post.Public {
// this is required for mapping // this is required for mapping
// posts to the author // posts to the author
shareable := models.Shareable{ shareable := models.Shareable{
...@@ -236,12 +229,8 @@ func (p ApiPost) Create() revel.Result { ...@@ -236,12 +229,8 @@ func (p ApiPost) Create() revel.Result {
} }
} }
dispatcher := jobs.Dispatcher{ // federate all the things \m/
User: p.CurrentUser, run.Now(jobs.Dispatcher{User: p.CurrentUser, Message: post})
Message: entity,
Model: post,
}
go dispatcher.Run()
return p.RenderJSON( return p.RenderJSON(
apiHelpers.SelectStructFields(post, fields)) apiHelpers.SelectStructFields(post, fields))
...@@ -361,15 +350,8 @@ func (p ApiPost) Delete(guid string) revel.Result { ...@@ -361,15 +350,8 @@ func (p ApiPost) Delete(guid string) revel.Result {
} }
if post.Person.ID == p.CurrentUser.Person.ID { if post.Person.ID == p.CurrentUser.Person.ID {
entity := federation.EntityRetraction{
Author: p.CurrentUser.Person.Author,
TargetGuid: post.Guid,
TargetType: models.ShareablePost,
}
dispatcher := jobs.Dispatcher{ dispatcher := jobs.Dispatcher{
User: p.CurrentUser, User: p.CurrentUser, Retract: true, Message: post}
Message: entity,
}
// NOTE relay to other hosts if we own this entity // NOTE relay to other hosts if we own this entity
// should be done before we start deleting db records // should be done before we start deleting db records
// thats why this call is synchron XXX we should // thats why this call is synchron XXX we should
...@@ -455,26 +437,22 @@ func (p ApiPost) Reshare() revel.Result { ...@@ -455,26 +437,22 @@ func (p ApiPost) Reshare() revel.Result {
return p.ApiError(http.StatusInternalServerError, ERR_SERVER) return p.ApiError(http.StatusInternalServerError, ERR_SERVER)
} }
entity := federation.EntityReshare{ post := models.Post{
Author: p.CurrentUser.Person.Author, PersonID: p.CurrentUser.Person.ID,
CreatedAt: time.Now(),
Public: true, // reshare is always public
Guid: guid, Guid: guid,
RootAuthor: rootPost.Person.Author, Type: models.Reshare,
RootGuid: rootPost.Guid, RootPersonID: rootPost.Person.ID,
RootGuid: &rootPost.Guid,
} }
entity.CreatedAt.New(time.Now())
var post models.Post err = post.Create()
err = post.Create(&entity)
if err != nil { if err != nil {
p.Log.Error(TAG, "db", err, "api", ERR_SERVER) p.Log.Error(TAG, "db", err, "api", ERR_SERVER)
return p.ApiError(http.StatusInternalServerError, ERR_SERVER) return p.ApiError(http.StatusInternalServerError, ERR_SERVER)
} }
dispatcher := jobs.Dispatcher{ run.Now(jobs.Dispatcher{User: p.CurrentUser, Message: post})
User: p.CurrentUser,
Model: post,
Message: entity,
}
go dispatcher.Run()
return p.RenderJSON(post) return p.RenderJSON(post)
} }
...@@ -8,8 +8,19 @@ import ( ...@@ -8,8 +8,19 @@ import (
"github.com/revel/revel" "github.com/revel/revel"
) )
var (
API_VERSION string
PROTO string
ADDRESS string
)
func init() { func init() {
revel.OnAppStart(func() { revel.OnAppStart(func() {
revel.Config.SetSection("ganggo")
API_VERSION = revel.Config.StringDefault("api.version", "v0")
PROTO = revel.Config.StringDefault("proto", "http://")
ADDRESS = revel.Config.StringDefault("address", "localhost")
revel.INFO.Println("Restful API loaded.") revel.INFO.Println("Restful API loaded.")
}) })
} }
# Restful API routes # Restful API routes
## ActivityPub
* /api/v0/ap/inbox ApiApUser.Inbox
* /api/v0/ap/user/:username/outbox ApiApUser.Outbox
POST /api/v0/ap/user/:username/inbox ApiApUser.Inbox
* /api/v0/ap/user/:username/following ApiApUser.Following
* /api/v0/ap/user/:username/followers ApiApUser.Followers
* /api/v0/ap/user/:username/actor ApiApUser.Actor
## GangGo API
POST /api/v0/oauth/tokens ApiOAuth.Create POST /api/v0/oauth/tokens ApiOAuth.Create
DELETE /api/v0/oauth/tokens/:id ApiOAuth.Delete DELETE /api/v0/oauth/tokens/:id ApiOAuth.Delete
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment