Cleanup
parent
76f9e14b93
commit
5fc3b48e70
19
TODO
19
TODO
|
@ -13,7 +13,7 @@
|
||||||
[✔] Handle being followed
|
[✔] Handle being followed
|
||||||
[✔] When followed, the handler should write the new follower to file
|
[✔] When followed, the handler should write the new follower to file
|
||||||
[✔] Make sure we send our boosts to all our followers
|
[✔] Make sure we send our boosts to all our followers
|
||||||
[ ] Write incoming activities to disk (do we have to?)
|
[x] Write incoming activities to disk (do we have to?)
|
||||||
[✔] Write all the announcements (boosts) to the database to
|
[✔] Write all the announcements (boosts) to the database to
|
||||||
their correct actors
|
their correct actors
|
||||||
[✔] Check if we are already following users
|
[✔] Check if we are already following users
|
||||||
|
@ -22,12 +22,13 @@
|
||||||
[✔] Make OS-independent (mosty directory separators)
|
[✔] Make OS-independent (mosty directory separators)
|
||||||
[✔] Create outbox.json programmatically
|
[✔] Create outbox.json programmatically
|
||||||
[✔] Make storage configurable (search for "storage" in project)
|
[✔] Make storage configurable (search for "storage" in project)
|
||||||
[ ] Check if we're boosting only stuff from actors we follow, not whatever comes
|
[✔] Check if we're boosting only stuff from actors we follow, not whatever comes
|
||||||
through in our inbox
|
through in our inbox
|
||||||
[✔] Boost not only articles but other things too
|
[✔] Boost not only articles but other things too
|
||||||
[✔] Sanitize input, never allow slashes or dots
|
[✔] Sanitize input, never allow slashes or dots
|
||||||
[✔] Add summary to actors.json
|
[✔] Add summary to actors.json
|
||||||
[ ] Check local actor names for characters illegal for filenames and ban them
|
[✔] Check local actor names for characters illegal for filenames and ban them
|
||||||
|
(Done in pherephone, not activityserve)
|
||||||
[✔] Create debug flag
|
[✔] Create debug flag
|
||||||
[✔] Write to following only upon accept
|
[✔] Write to following only upon accept
|
||||||
(waiting to actually get an accept so that I can test this)
|
(waiting to actually get an accept so that I can test this)
|
||||||
|
@ -41,16 +42,16 @@
|
||||||
[ ] Implement nodeinfo and statistics
|
[ ] Implement nodeinfo and statistics
|
||||||
[✔] Accept even if already follows us
|
[✔] Accept even if already follows us
|
||||||
[✔] Handle paging
|
[✔] Handle paging
|
||||||
[ ] Test paging
|
[✔] Test paging
|
||||||
[✔] Handle http signatures
|
[✔] Handle http signatures
|
||||||
[ ] Verify http signatures
|
[ ] Verify http signatures
|
||||||
[ ] Refactor, comment and clean up
|
[✔] Refactor, comment and clean up
|
||||||
[ ] Split to pherephone and activityServe
|
[✔] Split to pherephone and activityServe
|
||||||
[ ] Decide what's to be done with actors removed from `actors.json`.
|
[ ] Decide what's to be done with actors removed from `actors.json`.
|
||||||
[ ] Remove them?
|
[ ] Remove them?
|
||||||
[ ] Leave them read-only?
|
[ ] Leave them read-only?
|
||||||
[ ] Leave them as is?
|
[✔] Leave them as is?
|
||||||
[✔] Handle followers and following uri's
|
[✔] Handle followers and following uri's
|
||||||
[ ] Do I care about the inbox?
|
[ ] Do I care about the inbox?
|
||||||
[ ] Expose configuration to apps
|
[✔] Expose configuration to apps
|
||||||
[ ] Do not boost replies (configurable)
|
[✔] Do not boost replies (configurable)
|
79
actor.go
79
actor.go
|
@ -54,8 +54,8 @@ type ActorToSave struct {
|
||||||
Followers, Following, Rejected, Requested map[string]interface{}
|
Followers, Following, Rejected, Requested map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeActor returns a new local actor we can act
|
// MakeActor creates and returns a new local actor we can act
|
||||||
// on behalf of
|
// on behalf of. It also creates its files on disk
|
||||||
func MakeActor(name, summary, actorType string) (Actor, error) {
|
func MakeActor(name, summary, actorType string) (Actor, error) {
|
||||||
followers := make(map[string]interface{})
|
followers := make(map[string]interface{})
|
||||||
following := make(map[string]interface{})
|
following := make(map[string]interface{})
|
||||||
|
@ -125,7 +125,7 @@ func MakeActor(name, summary, actorType string) (Actor, error) {
|
||||||
return actor, nil
|
return actor, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOutboxIRI returns the outbox iri in net/url
|
// GetOutboxIRI returns the outbox iri in net/url format
|
||||||
func (a *Actor) GetOutboxIRI() *url.URL {
|
func (a *Actor) GetOutboxIRI() *url.URL {
|
||||||
iri := a.iri + "/outbox"
|
iri := a.iri + "/outbox"
|
||||||
nuiri, _ := url.Parse(iri)
|
nuiri, _ := url.Parse(iri)
|
||||||
|
@ -133,7 +133,8 @@ func (a *Actor) GetOutboxIRI() *url.URL {
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadActor searches the filesystem and creates an Actor
|
// LoadActor searches the filesystem and creates an Actor
|
||||||
// from the data in name.json
|
// from the data in <name>.json
|
||||||
|
// This does not preserve events so use with caution
|
||||||
func LoadActor(name string) (Actor, error) {
|
func LoadActor(name string) (Actor, error) {
|
||||||
// make sure our users can't read our hard drive
|
// make sure our users can't read our hard drive
|
||||||
if strings.ContainsAny(name, "./ ") {
|
if strings.ContainsAny(name, "./ ") {
|
||||||
|
@ -161,9 +162,6 @@ func LoadActor(name string) (Actor, error) {
|
||||||
return Actor{}, err
|
return Actor{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// publicKeyNewLines := strings.ReplaceAll(jsonData["PublicKey"].(string), "\\n", "\n")
|
|
||||||
// privateKeyNewLines := strings.ReplaceAll(jsonData["PrivateKey"].(string), "\\n", "\n")
|
|
||||||
|
|
||||||
publicKeyDecoded, rest := pem.Decode([]byte(jsonData["PublicKey"].(string)))
|
publicKeyDecoded, rest := pem.Decode([]byte(jsonData["PublicKey"].(string)))
|
||||||
if publicKeyDecoded == nil {
|
if publicKeyDecoded == nil {
|
||||||
log.Info(rest)
|
log.Info(rest)
|
||||||
|
@ -245,7 +243,7 @@ func GetActor(name, summary, actorType string) (Actor, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// func LoadActorFromIRI(iri string) a Actor{
|
// func LoadActorFromIRI(iri string) a Actor{
|
||||||
|
// TODO, this should parse the iri and load the right actor
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// save the actor to file
|
// save the actor to file
|
||||||
|
@ -445,6 +443,9 @@ func (a *Actor) GetFollowing(page int) (response []byte, err error) {
|
||||||
return a.getPeers(page, "following")
|
return a.getPeers(page, "following")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// signedHTTPPost performs an HTTP post on behalf of Actor with the
|
||||||
|
// request-target, date, host and digest headers signed
|
||||||
|
// with the actor's private key.
|
||||||
func (a *Actor) signedHTTPPost(content map[string]interface{}, to string) (err error) {
|
func (a *Actor) signedHTTPPost(content map[string]interface{}, to string) (err error) {
|
||||||
b, err := json.Marshal(content)
|
b, err := json.Marshal(content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -498,7 +499,7 @@ func (a *Actor) signedHTTPPost(content map[string]interface{}, to string) (err e
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
responseData, _ := ioutil.ReadAll(resp.Body)
|
responseData, _ := ioutil.ReadAll(resp.Body)
|
||||||
fmt.Printf("POST request to %s succeeded (%d): %s \nResponse: %s \nRequest: %s \nHeaders: %s", to, resp.StatusCode, resp.Status, FormatJSON(responseData), FormatJSON(byteCopy), FormatHeaders(req.Header))
|
log.Errorf("POST request to %s succeeded (%d): %s \nResponse: %s \nRequest: %s \nHeaders: %s", to, resp.StatusCode, resp.Status, FormatJSON(responseData), FormatJSON(byteCopy), FormatHeaders(req.Header))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -556,6 +557,8 @@ func (a *Actor) NewFollower(iri string, inbox string) error {
|
||||||
return a.save()
|
return a.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// appendToOutbox adds a new line with the id of the activity
|
||||||
|
// to outbox.txt
|
||||||
func (a *Actor) appendToOutbox(iri string) (err error) {
|
func (a *Actor) appendToOutbox(iri string) (err error) {
|
||||||
// create outbox file if it doesn't exist
|
// create outbox file if it doesn't exist
|
||||||
var outbox *os.File
|
var outbox *os.File
|
||||||
|
@ -574,6 +577,7 @@ func (a *Actor) appendToOutbox(iri string) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// batchSend sends a batch of http posts to a list of recipients
|
||||||
func (a *Actor) batchSend(activity map[string]interface{}, recipients []string) (err error) {
|
func (a *Actor) batchSend(activity map[string]interface{}, recipients []string) (err error) {
|
||||||
for _, v := range recipients {
|
for _, v := range recipients {
|
||||||
err := a.signedHTTPPost(activity, v)
|
err := a.signedHTTPPost(activity, v)
|
||||||
|
@ -584,6 +588,7 @@ func (a *Actor) batchSend(activity map[string]interface{}, recipients []string)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// send to followers sends a batch of http posts to each one of the followers
|
||||||
func (a *Actor) sendToFollowers(activity map[string]interface{}) (err error) {
|
func (a *Actor) sendToFollowers(activity map[string]interface{}) (err error) {
|
||||||
recipients := make([]string, len(a.followers))
|
recipients := make([]string, len(a.followers))
|
||||||
|
|
||||||
|
@ -645,15 +650,34 @@ func (a *Actor) Follow(user string) (err error) {
|
||||||
// was accepted when initially following that user
|
// was accepted when initially following that user
|
||||||
// (this is read from the `actor.following` map
|
// (this is read from the `actor.following` map
|
||||||
func (a *Actor) Unfollow(user string) {
|
func (a *Actor) Unfollow(user string) {
|
||||||
|
// if we have a request to follow this user cancel it
|
||||||
|
cancelRequest := false
|
||||||
|
if _, ok := a.requested[user]; ok {
|
||||||
|
log.Info("Cancelling follow request")
|
||||||
|
cancelRequest = true
|
||||||
|
// then continue to send the unfollow to the receipient
|
||||||
|
// to inform them that the request is cancelled.
|
||||||
|
} else if _, ok := a.following[user]; !ok {
|
||||||
|
log.Info("We are not following this user, ignoring...")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
log.Info("Unfollowing " + user)
|
log.Info("Unfollowing " + user)
|
||||||
|
|
||||||
|
var hash string
|
||||||
|
// find the id of the original follow
|
||||||
|
if cancelRequest {
|
||||||
|
hash = a.requested[user].(string)
|
||||||
|
} else {
|
||||||
|
hash = a.following[user].(string)
|
||||||
|
}
|
||||||
|
|
||||||
// create an undo activiy
|
// create an undo activiy
|
||||||
undo := make(map[string]interface{})
|
undo := make(map[string]interface{})
|
||||||
undo["@context"] = context()
|
undo["@context"] = context()
|
||||||
undo["actor"] = a.iri
|
undo["actor"] = a.iri
|
||||||
|
undo["id"] = baseURL + "/item/" + hash + "/undo"
|
||||||
// find the id of the original follow
|
undo["type"] = "Undo"
|
||||||
hash := a.following[user].(string)
|
|
||||||
|
|
||||||
follow := make(map[string]interface{})
|
follow := make(map[string]interface{})
|
||||||
|
|
||||||
|
@ -673,22 +697,23 @@ func (a *Actor) Unfollow(user string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// only if we're already following them
|
PrettyPrint(undo)
|
||||||
if _, ok := a.following[user]; ok {
|
go func() {
|
||||||
PrettyPrint(undo)
|
err := a.signedHTTPPost(undo, remoteUser.inbox)
|
||||||
go func() {
|
if err != nil {
|
||||||
err := a.signedHTTPPost(undo, remoteUser.inbox)
|
log.Info("Couldn't unfollow " + user)
|
||||||
if err != nil {
|
log.Info(err)
|
||||||
log.Info("Couldn't unfollow " + user)
|
return
|
||||||
log.Info(err)
|
}
|
||||||
return
|
// if there was no error then delete the follow
|
||||||
}
|
// from the list
|
||||||
// if there was no error then delete the follow
|
if cancelRequest {
|
||||||
// from the list
|
delete(a.requested, user)
|
||||||
|
} else {
|
||||||
delete(a.following, user)
|
delete(a.following, user)
|
||||||
a.save()
|
}
|
||||||
}()
|
a.save()
|
||||||
}
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Announce this activity to our followers
|
// Announce this activity to our followers
|
||||||
|
|
1
http.go
1
http.go
|
@ -251,6 +251,7 @@ func Serve(actors map[string]Actor) {
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
actor.following[acceptor] = hash
|
actor.following[acceptor] = hash
|
||||||
|
PrettyPrint(activity)
|
||||||
delete(actor.requested, acceptor)
|
delete(actor.requested, acceptor)
|
||||||
actor.save()
|
actor.save()
|
||||||
case "Reject":
|
case "Reject":
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
## A very light ActivityPub library in go
|
## A very light ActivityPub library in go
|
||||||
|
|
||||||
This library was built to support the very little functions that [pherephone](https://github.com/writeas/pherephone) requires. It might never be feature-complete but it's a very good point to start your activityPub journey. Take a look at [activityserve-example] for a simple main file that uses **activityserve** to post a "Hello, world" message.
|
This library was built to support the very little functions that [pherephone](https://github.com/writeas/pherephone) requires. It might never be feature-complete but it's a very good point to start your activityPub journey. Take a look at [activityserve-example](https://github.com/writeas/activityserve-example) for a simple main file that uses **activityserve** to post a "Hello, world" message.
|
||||||
|
|
||||||
For now it supports following and unfollowing users, accepting follows, announcing (boosting) other posts and this is pretty much it.
|
For now it supports following and unfollowing users, accepting follows, announcing (boosting) other posts and this is pretty much it.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue