Fixes to enable following pixelfed
add requested list in actor (holds the follow requests that haven't been rejected or accepted yet), load actor from memory instead of disk when there's a new activity in our inbox and other minor fixes
This commit is contained in:
parent
62d04be12e
commit
6a02d08d5d
5 changed files with 109 additions and 55 deletions
4
TODO
4
TODO
|
@ -14,7 +14,7 @@
|
||||||
[✔] 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?)
|
[ ] 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
|
||||||
[✔] On GetOutbox read the database and present a list of the
|
[✔] On GetOutbox read the database and present a list of the
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
[ ] 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
|
||||||
[✔] Create debug flag
|
[✔] Create debug flag
|
||||||
|
|
93
actor.go
93
actor.go
|
@ -30,10 +30,11 @@ import (
|
||||||
// Actor represents a local actor we can act on
|
// Actor represents a local actor we can act on
|
||||||
// behalf of.
|
// behalf of.
|
||||||
type Actor struct {
|
type Actor struct {
|
||||||
name, summary, actorType, iri string
|
Name, summary, actorType, iri string
|
||||||
followersIRI string
|
followersIRI string
|
||||||
nuIri *url.URL
|
nuIri *url.URL
|
||||||
followers, following, rejected map[string]interface{}
|
followers, following, rejected map[string]interface{}
|
||||||
|
requested map[string]interface{}
|
||||||
posts map[int]map[string]interface{}
|
posts map[int]map[string]interface{}
|
||||||
publicKey crypto.PublicKey
|
publicKey crypto.PublicKey
|
||||||
privateKey crypto.PrivateKey
|
privateKey crypto.PrivateKey
|
||||||
|
@ -41,6 +42,7 @@ type Actor struct {
|
||||||
privateKeyPem string
|
privateKeyPem string
|
||||||
publicKeyID string
|
publicKeyID string
|
||||||
OnFollow func(map[string]interface{})
|
OnFollow func(map[string]interface{})
|
||||||
|
OnReceiveContent func(map[string]interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActorToSave is a stripped down actor representation
|
// ActorToSave is a stripped down actor representation
|
||||||
|
@ -49,7 +51,7 @@ type Actor struct {
|
||||||
// see https://stackoverflow.com/questions/26327391/json-marshalstruct-returns
|
// see https://stackoverflow.com/questions/26327391/json-marshalstruct-returns
|
||||||
type ActorToSave struct {
|
type ActorToSave struct {
|
||||||
Name, Summary, ActorType, IRI, PublicKey, PrivateKey string
|
Name, Summary, ActorType, IRI, PublicKey, PrivateKey string
|
||||||
Followers, Following, Rejected map[string]interface{}
|
Followers, Following, Rejected, Requested map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeActor returns a new local actor we can act
|
// MakeActor returns a new local actor we can act
|
||||||
|
@ -58,6 +60,7 @@ 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{})
|
||||||
rejected := make(map[string]interface{})
|
rejected := make(map[string]interface{})
|
||||||
|
requested := make(map[string]interface{})
|
||||||
followersIRI := baseURL + name + "/followers"
|
followersIRI := baseURL + name + "/followers"
|
||||||
publicKeyID := baseURL + name + "#main-key"
|
publicKeyID := baseURL + name + "#main-key"
|
||||||
iri := baseURL + name
|
iri := baseURL + name
|
||||||
|
@ -67,7 +70,7 @@ func MakeActor(name, summary, actorType string) (Actor, error) {
|
||||||
return Actor{}, err
|
return Actor{}, err
|
||||||
}
|
}
|
||||||
actor := Actor{
|
actor := Actor{
|
||||||
name: name,
|
Name: name,
|
||||||
summary: summary,
|
summary: summary,
|
||||||
actorType: actorType,
|
actorType: actorType,
|
||||||
iri: iri,
|
iri: iri,
|
||||||
|
@ -75,12 +78,14 @@ func MakeActor(name, summary, actorType string) (Actor, error) {
|
||||||
followers: followers,
|
followers: followers,
|
||||||
following: following,
|
following: following,
|
||||||
rejected: rejected,
|
rejected: rejected,
|
||||||
|
requested: requested,
|
||||||
followersIRI: followersIRI,
|
followersIRI: followersIRI,
|
||||||
publicKeyID: publicKeyID,
|
publicKeyID: publicKeyID,
|
||||||
}
|
}
|
||||||
|
|
||||||
// set auto accept by default (this could be a configuration value)
|
// set auto accept by default (this could be a configuration value)
|
||||||
actor.OnFollow = func(activity map[string]interface{}) { actor.Accept(activity) }
|
actor.OnFollow = func(activity map[string]interface{}) { actor.Accept(activity) }
|
||||||
|
actor.OnReceiveContent = func(activity map[string]interface{}) {}
|
||||||
|
|
||||||
// create actor's keypair
|
// create actor's keypair
|
||||||
rng := rand.Reader
|
rng := rand.Reader
|
||||||
|
@ -138,6 +143,7 @@ func LoadActor(name string) (Actor, error) {
|
||||||
jsonFile := storage + slash + "actors" + slash + name + slash + name + ".json"
|
jsonFile := storage + slash + "actors" + slash + name + slash + name + ".json"
|
||||||
fileHandle, err := os.Open(jsonFile)
|
fileHandle, err := os.Open(jsonFile)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
|
log.Info(name)
|
||||||
log.Info("We don't have this kind of actor stored")
|
log.Info("We don't have this kind of actor stored")
|
||||||
return Actor{}, err
|
return Actor{}, err
|
||||||
}
|
}
|
||||||
|
@ -182,7 +188,7 @@ func LoadActor(name string) (Actor, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
actor := Actor{
|
actor := Actor{
|
||||||
name: name,
|
Name: name,
|
||||||
summary: jsonData["Summary"].(string),
|
summary: jsonData["Summary"].(string),
|
||||||
actorType: jsonData["ActorType"].(string),
|
actorType: jsonData["ActorType"].(string),
|
||||||
iri: jsonData["IRI"].(string),
|
iri: jsonData["IRI"].(string),
|
||||||
|
@ -190,6 +196,7 @@ func LoadActor(name string) (Actor, error) {
|
||||||
followers: jsonData["Followers"].(map[string]interface{}),
|
followers: jsonData["Followers"].(map[string]interface{}),
|
||||||
following: jsonData["Following"].(map[string]interface{}),
|
following: jsonData["Following"].(map[string]interface{}),
|
||||||
rejected: jsonData["Rejected"].(map[string]interface{}),
|
rejected: jsonData["Rejected"].(map[string]interface{}),
|
||||||
|
requested: jsonData["Requested"].(map[string]interface{}),
|
||||||
publicKey: publicKey,
|
publicKey: publicKey,
|
||||||
privateKey: privateKey,
|
privateKey: privateKey,
|
||||||
publicKeyPem: jsonData["PublicKey"].(string),
|
publicKeyPem: jsonData["PublicKey"].(string),
|
||||||
|
@ -199,6 +206,7 @@ func LoadActor(name string) (Actor, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
actor.OnFollow = func(activity map[string]interface{}) { actor.Accept(activity) }
|
actor.OnFollow = func(activity map[string]interface{}) { actor.Accept(activity) }
|
||||||
|
actor.OnReceiveContent = func(activity map[string]interface{}) {}
|
||||||
|
|
||||||
return actor, nil
|
return actor, nil
|
||||||
}
|
}
|
||||||
|
@ -245,19 +253,20 @@ func (a *Actor) save() error {
|
||||||
|
|
||||||
// check if we already have a directory to save actors
|
// check if we already have a directory to save actors
|
||||||
// and if not, create it
|
// and if not, create it
|
||||||
dir := storage + slash + "actors" + slash + a.name + slash + "items"
|
dir := storage + slash + "actors" + slash + a.Name + slash + "items"
|
||||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||||
os.MkdirAll(dir, 0755)
|
os.MkdirAll(dir, 0755)
|
||||||
}
|
}
|
||||||
|
|
||||||
actorToSave := ActorToSave{
|
actorToSave := ActorToSave{
|
||||||
Name: a.name,
|
Name: a.Name,
|
||||||
Summary: a.summary,
|
Summary: a.summary,
|
||||||
ActorType: a.actorType,
|
ActorType: a.actorType,
|
||||||
IRI: a.iri,
|
IRI: a.iri,
|
||||||
Followers: a.followers,
|
Followers: a.followers,
|
||||||
Following: a.following,
|
Following: a.following,
|
||||||
Rejected: a.rejected,
|
Rejected: a.rejected,
|
||||||
|
Requested: a.requested,
|
||||||
PublicKey: a.publicKeyPem,
|
PublicKey: a.publicKeyPem,
|
||||||
PrivateKey: a.privateKeyPem,
|
PrivateKey: a.privateKeyPem,
|
||||||
}
|
}
|
||||||
|
@ -269,7 +278,7 @@ func (a *Actor) save() error {
|
||||||
}
|
}
|
||||||
// log.Info(actorToSave)
|
// log.Info(actorToSave)
|
||||||
// log.Info(string(actorJSON))
|
// log.Info(string(actorJSON))
|
||||||
err = ioutil.WriteFile(storage+slash+"actors"+slash+a.name+slash+a.name+".json", actorJSON, 0644)
|
err = ioutil.WriteFile(storage+slash+"actors"+slash+a.Name+slash+a.Name+".json", actorJSON, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("WriteFileJson ERROR: %+v", err)
|
log.Printf("WriteFileJson ERROR: %+v", err)
|
||||||
return err
|
return err
|
||||||
|
@ -279,19 +288,19 @@ func (a *Actor) save() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Actor) whoAmI() string {
|
func (a *Actor) whoAmI() string {
|
||||||
return `{"@context": "https://www.w3.org/ns/activitystreams",
|
return `{"@context":["https://www.w3.org/ns/activitystreams"],
|
||||||
"type": "` + a.actorType + `",
|
"type": "` + a.actorType + `",
|
||||||
"id": "` + baseURL + a.name + `",
|
"id": "` + baseURL + a.Name + `",
|
||||||
"name": "` + a.name + `",
|
"name": "` + a.Name + `",
|
||||||
"preferredUsername": "` + a.name + `",
|
"preferredUsername": "` + a.Name + `",
|
||||||
"summary": "` + a.summary + `",
|
"summary": "` + a.summary + `",
|
||||||
"inbox": "` + baseURL + a.name + `/inbox",
|
"inbox": "` + baseURL + a.Name + `/inbox",
|
||||||
"outbox": "` + baseURL + a.name + `/outbox",
|
"outbox": "` + baseURL + a.Name + `/outbox",
|
||||||
"followers": "` + baseURL + a.name + `/peers/followers",
|
"followers": "` + baseURL + a.Name + `/peers/followers",
|
||||||
"following": "` + baseURL + a.name + `/peers/following",
|
"following": "` + baseURL + a.Name + `/peers/following",
|
||||||
"publicKey": {
|
"publicKey": {
|
||||||
"id": "` + baseURL + a.name + `#main-key",
|
"id": "` + baseURL + a.Name + `#main-key",
|
||||||
"owner": "` + baseURL + a.name + `",
|
"owner": "` + baseURL + a.Name + `",
|
||||||
"publicKeyPem": "` + strings.ReplaceAll(a.publicKeyPem, "\n", "\\n") + `"
|
"publicKeyPem": "` + strings.ReplaceAll(a.publicKeyPem, "\n", "\\n") + `"
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
@ -299,12 +308,12 @@ func (a *Actor) whoAmI() string {
|
||||||
|
|
||||||
func (a *Actor) newItemID() (hash string, url string) {
|
func (a *Actor) newItemID() (hash string, url string) {
|
||||||
hash = uniuri.New()
|
hash = uniuri.New()
|
||||||
return hash, baseURL + a.name + "/item/" + hash
|
return hash, baseURL + a.Name + "/item/" + hash
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Actor) newID() (hash string, url string) {
|
func (a *Actor) newID() (hash string, url string) {
|
||||||
hash = uniuri.New()
|
hash = uniuri.New()
|
||||||
return hash, baseURL + a.name + "/" + hash
|
return hash, baseURL + a.Name + "/" + hash
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Reply(content string, inReplyTo string)
|
// TODO Reply(content string, inReplyTo string)
|
||||||
|
@ -324,11 +333,11 @@ func (a *Actor) CreateNote(content, inReplyTo string) {
|
||||||
create := make(map[string]interface{})
|
create := make(map[string]interface{})
|
||||||
note := make(map[string]interface{})
|
note := make(map[string]interface{})
|
||||||
create["@context"] = context()
|
create["@context"] = context()
|
||||||
create["actor"] = baseURL + a.name
|
create["actor"] = baseURL + a.Name
|
||||||
create["cc"] = a.followersIRI
|
create["cc"] = a.followersIRI
|
||||||
create["id"] = id
|
create["id"] = id
|
||||||
create["object"] = note
|
create["object"] = note
|
||||||
note["attributedTo"] = baseURL + a.name
|
note["attributedTo"] = baseURL + a.Name
|
||||||
note["cc"] = a.followersIRI
|
note["cc"] = a.followersIRI
|
||||||
note["content"] = content
|
note["content"] = content
|
||||||
if inReplyTo != "" {
|
if inReplyTo != "" {
|
||||||
|
@ -357,7 +366,7 @@ func (a *Actor) CreateNote(content, inReplyTo string) {
|
||||||
func (a *Actor) saveItem(hash string, content map[string]interface{}) error {
|
func (a *Actor) saveItem(hash string, content map[string]interface{}) error {
|
||||||
JSON, _ := json.MarshalIndent(content, "", "\t")
|
JSON, _ := json.MarshalIndent(content, "", "\t")
|
||||||
|
|
||||||
dir := storage + slash + "actors" + slash + a.name + slash + "items"
|
dir := storage + slash + "actors" + slash + a.Name + slash + "items"
|
||||||
err := ioutil.WriteFile(dir+slash+hash+".json", JSON, 0644)
|
err := ioutil.WriteFile(dir+slash+hash+".json", JSON, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("WriteFileJson ERROR: %+v", err)
|
log.Printf("WriteFileJson ERROR: %+v", err)
|
||||||
|
@ -367,7 +376,7 @@ func (a *Actor) saveItem(hash string, content map[string]interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Actor) loadItem(hash string) (item map[string]interface{}, err error) {
|
func (a *Actor) loadItem(hash string) (item map[string]interface{}, err error) {
|
||||||
dir := storage + slash + "actors" + slash + a.name + slash + "items"
|
dir := storage + slash + "actors" + slash + a.Name + slash + "items"
|
||||||
jsonFile := dir + slash + hash + ".json"
|
jsonFile := dir + slash + hash + ".json"
|
||||||
fileHandle, err := os.Open(jsonFile)
|
fileHandle, err := os.Open(jsonFile)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
|
@ -405,20 +414,20 @@ func (a *Actor) getPeers(page int, who string) (response []byte, err error) {
|
||||||
return nil, errors.New("cannot find collection" + who)
|
return nil, errors.New("cannot find collection" + who)
|
||||||
}
|
}
|
||||||
themap := make(map[string]interface{})
|
themap := make(map[string]interface{})
|
||||||
themap["@context"] = "https://www.w3.org/ns/activitystreams"
|
themap["@context"] = context()
|
||||||
if page == 0 {
|
if page == 0 {
|
||||||
themap["first"] = baseURL + a.name + "/" + who + "?page=1"
|
themap["first"] = baseURL + a.Name + "/peers/" + who + "?page=1"
|
||||||
themap["id"] = baseURL + a.name + "/" + who
|
themap["id"] = baseURL + a.Name + "/peers/" + who
|
||||||
themap["totalItems"] = strconv.Itoa(len(collection))
|
themap["totalItems"] = strconv.Itoa(len(collection))
|
||||||
themap["type"] = "OrderedCollection"
|
themap["type"] = "OrderedCollection"
|
||||||
} else if page == 1 { // implement pagination
|
} else if page == 1 { // implement pagination
|
||||||
themap["id"] = baseURL + a.name + who + "?page=" + strconv.Itoa(page)
|
themap["id"] = baseURL + a.Name + who + "?page=" + strconv.Itoa(page)
|
||||||
items := make([]string, 0, len(collection))
|
items := make([]string, 0, len(collection))
|
||||||
for k := range collection {
|
for k := range collection {
|
||||||
items = append(items, k)
|
items = append(items, k)
|
||||||
}
|
}
|
||||||
themap["orderedItems"] = items
|
themap["orderedItems"] = items
|
||||||
themap["partOf"] = baseURL + a.name + "/" + who
|
themap["partOf"] = baseURL + a.Name + "/peers/" + who
|
||||||
themap["totalItems"] = len(collection)
|
themap["totalItems"] = len(collection)
|
||||||
themap["type"] = "OrderedCollectionPage"
|
themap["type"] = "OrderedCollectionPage"
|
||||||
}
|
}
|
||||||
|
@ -465,7 +474,8 @@ func (a *Actor) signedHTTPPost(content map[string]interface{}, to string) (err e
|
||||||
req.Header.Add("Date", time.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05")+" GMT")
|
req.Header.Add("Date", time.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05")+" GMT")
|
||||||
req.Header.Add("User-Agent", userAgent+" "+version)
|
req.Header.Add("User-Agent", userAgent+" "+version)
|
||||||
req.Header.Add("Host", iri.Host)
|
req.Header.Add("Host", iri.Host)
|
||||||
req.Header.Add("Accept", "application/activity+json")
|
req.Header.Add("Accept", "application/activity+json; charset=utf-8")
|
||||||
|
req.Header.Add("Content-Type", "application/activity+json; charset=utf-8")
|
||||||
sum := sha256.Sum256(b)
|
sum := sha256.Sum256(b)
|
||||||
req.Header.Add("Digest",
|
req.Header.Add("Digest",
|
||||||
fmt.Sprintf("SHA-256=%s",
|
fmt.Sprintf("SHA-256=%s",
|
||||||
|
@ -550,7 +560,7 @@ 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
|
||||||
|
|
||||||
outboxFilePath := storage + slash + "actors" + slash + a.name + slash + "outbox.txt"
|
outboxFilePath := storage + slash + "actors" + slash + a.Name + slash + "outbox.txt"
|
||||||
outbox, err = os.OpenFile(outboxFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
outbox, err = os.OpenFile(outboxFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Info("Cannot create or open outbox file")
|
log.Info("Cannot create or open outbox file")
|
||||||
|
@ -616,7 +626,10 @@ func (a *Actor) Follow(user string) (err error) {
|
||||||
}
|
}
|
||||||
// save the activity
|
// save the activity
|
||||||
a.saveItem(hash, follow)
|
a.saveItem(hash, follow)
|
||||||
// we are going to save only on accept so look at
|
a.requested[user] = hash
|
||||||
|
a.save()
|
||||||
|
// we are going to save the request here
|
||||||
|
// and save the follow only on accept so look at
|
||||||
// the http handler for the accept code
|
// the http handler for the accept code
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
@ -760,3 +773,21 @@ func (a *Actor) Accept(follow map[string]interface{}) {
|
||||||
go a.signedHTTPPost(accept, follower.inbox)
|
go a.signedHTTPPost(accept, follower.inbox)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Followers returns the list of followers
|
||||||
|
func (a *Actor) Followers() map[string]string {
|
||||||
|
f := make(map[string]string)
|
||||||
|
for follower, inbox := range a.followers {
|
||||||
|
f[follower] = inbox.(string)
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// Following returns the list of followers
|
||||||
|
func (a *Actor) Following() map[string]string {
|
||||||
|
f := make(map[string]string)
|
||||||
|
for followee, hash := range a.following {
|
||||||
|
f[followee] = hash.(string)
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
62
http.go
62
http.go
|
@ -14,7 +14,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Serve starts an http server with all the required handlers
|
// Serve starts an http server with all the required handlers
|
||||||
func Serve() {
|
func Serve(actors map[string]Actor) {
|
||||||
|
|
||||||
var webfingerHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
|
var webfingerHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("content-type", "application/jrd+json; charset=utf-8")
|
w.Header().Set("content-type", "application/jrd+json; charset=utf-8")
|
||||||
|
@ -35,13 +35,13 @@ func Serve() {
|
||||||
|
|
||||||
responseMap := make(map[string]interface{})
|
responseMap := make(map[string]interface{})
|
||||||
|
|
||||||
responseMap["subject"] = "acct:" + actor.name + "@" + server
|
responseMap["subject"] = "acct:" + actor.Name + "@" + server
|
||||||
// links is a json array with a single element
|
// links is a json array with a single element
|
||||||
var links [1]map[string]string
|
var links [1]map[string]string
|
||||||
link1 := make(map[string]string)
|
link1 := make(map[string]string)
|
||||||
link1["rel"] = "self"
|
link1["rel"] = "self"
|
||||||
link1["type"] = "application/activity+json"
|
link1["type"] = "application/activity+json"
|
||||||
link1["href"] = baseURL + actor.name
|
link1["href"] = baseURL + actor.Name
|
||||||
links[0] = link1
|
links[0] = link1
|
||||||
responseMap["links"] = links
|
responseMap["links"] = links
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ func Serve() {
|
||||||
}
|
}
|
||||||
postsPerPage := 100
|
postsPerPage := 100
|
||||||
var response []byte
|
var response []byte
|
||||||
filename := storage + slash + "actors" + slash + actor.name + slash + "outbox.txt"
|
filename := storage + slash + "actors" + slash + actor.Name + slash + "outbox.txt"
|
||||||
totalLines, err := lineCounter(filename)
|
totalLines, err := lineCounter(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Info("Can't read outbox.txt")
|
log.Info("Can't read outbox.txt")
|
||||||
|
@ -104,9 +104,9 @@ func Serve() {
|
||||||
//TODO fix total items
|
//TODO fix total items
|
||||||
response = []byte(`{
|
response = []byte(`{
|
||||||
"@context" : "https://www.w3.org/ns/activitystreams",
|
"@context" : "https://www.w3.org/ns/activitystreams",
|
||||||
"first" : "` + baseURL + actor.name + `/outbox?page=1",
|
"first" : "` + baseURL + actor.Name + `/outbox?page=1",
|
||||||
"id" : "` + baseURL + actor.name + `/outbox",
|
"id" : "` + baseURL + actor.Name + `/outbox",
|
||||||
"last" : "` + baseURL + actor.name + `/outbox?page=` + strconv.Itoa(totalLines/postsPerPage+1) + `",
|
"last" : "` + baseURL + actor.Name + `/outbox?page=` + strconv.Itoa(totalLines/postsPerPage+1) + `",
|
||||||
"totalItems" : ` + strconv.Itoa(totalLines) + `,
|
"totalItems" : ` + strconv.Itoa(totalLines) + `,
|
||||||
"type" : "OrderedCollection"
|
"type" : "OrderedCollection"
|
||||||
}`)
|
}`)
|
||||||
|
@ -124,15 +124,15 @@ func Serve() {
|
||||||
}
|
}
|
||||||
responseMap := make(map[string]interface{})
|
responseMap := make(map[string]interface{})
|
||||||
responseMap["@context"] = context()
|
responseMap["@context"] = context()
|
||||||
responseMap["id"] = baseURL + actor.name + "/outbox?page=" + pageStr
|
responseMap["id"] = baseURL + actor.Name + "/outbox?page=" + pageStr
|
||||||
|
|
||||||
if page*postsPerPage < totalLines {
|
if page*postsPerPage < totalLines {
|
||||||
responseMap["next"] = baseURL + actor.name + "/outbox?page=" + strconv.Itoa(page+1)
|
responseMap["next"] = baseURL + actor.Name + "/outbox?page=" + strconv.Itoa(page+1)
|
||||||
}
|
}
|
||||||
if page > 1 {
|
if page > 1 {
|
||||||
responseMap["prev"] = baseURL + actor.name + "/outbox?page=" + strconv.Itoa(page-1)
|
responseMap["prev"] = baseURL + actor.Name + "/outbox?page=" + strconv.Itoa(page-1)
|
||||||
}
|
}
|
||||||
responseMap["partOf"] = baseURL + actor.name + "/outbox"
|
responseMap["partOf"] = baseURL + actor.Name + "/outbox"
|
||||||
responseMap["type"] = "OrderedCollectionPage"
|
responseMap["type"] = "OrderedCollectionPage"
|
||||||
|
|
||||||
orderedItems := make([]interface{}, 0, postsPerPage)
|
orderedItems := make([]interface{}, 0, postsPerPage)
|
||||||
|
@ -144,7 +144,7 @@ func Serve() {
|
||||||
// keep the hash
|
// keep the hash
|
||||||
hash := parts[len(parts)-1]
|
hash := parts[len(parts)-1]
|
||||||
// build the filename
|
// build the filename
|
||||||
filename := storage + slash + "actors" + slash + actor.name + slash + "items" + slash + hash + ".json"
|
filename := storage + slash + "actors" + slash + actor.Name + slash + "items" + slash + hash + ".json"
|
||||||
// open the file
|
// open the file
|
||||||
activityJSON, err := ioutil.ReadFile(filename)
|
activityJSON, err := ioutil.ReadFile(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -208,31 +208,44 @@ func Serve() {
|
||||||
id := follow["id"].(string)
|
id := follow["id"].(string)
|
||||||
|
|
||||||
// check if the object of the follow is us
|
// check if the object of the follow is us
|
||||||
if follow["actor"].(string) != baseURL+actor.name {
|
if follow["actor"].(string) != baseURL+actor.Name {
|
||||||
log.Info("This is not for us, ignoring")
|
log.Info("This is not for us, ignoring")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// try to get the hash only
|
// try to get the hash only
|
||||||
hash := strings.Replace(id, baseURL+actor.name+"/item/", "", 1)
|
hash := strings.Replace(id, baseURL+actor.Name+"/item/", "", 1)
|
||||||
// if there are still slashes in the result this means the
|
// if there are still slashes in the result this means the
|
||||||
// above didn't work
|
// above didn't work
|
||||||
if strings.ContainsAny(hash, "/") {
|
if strings.ContainsAny(hash, "/") {
|
||||||
|
// log.Info(follow)
|
||||||
log.Info("The id of this follow is probably wrong")
|
log.Info("The id of this follow is probably wrong")
|
||||||
return
|
// we could return here but pixelfed returns
|
||||||
|
// the id as http://domain.tld/actor instead of
|
||||||
|
// http://domain.tld/actor/item/hash so this chokes
|
||||||
|
// return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Have we already requested this follow or are we following anybody that
|
// Have we already requested this follow or are we following anybody that
|
||||||
// sprays accepts?
|
// sprays accepts?
|
||||||
savedFollowRequest, err := actor.loadItem(hash)
|
|
||||||
if err != nil {
|
// pixelfed doesn't return the original follow thus the id is wrong so we
|
||||||
|
// need to just check if we requested this actor
|
||||||
|
|
||||||
|
// pixelfed doesn't return the original follow thus the id is wrong so we
|
||||||
|
// need to just check if we requested this actor
|
||||||
|
if _, ok := actor.requested[acceptor]; !ok {
|
||||||
log.Info("We never requested this follow, ignoring the Accept")
|
log.Info("We never requested this follow, ignoring the Accept")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if savedFollowRequest["id"] != id {
|
// if pixelfed fixes https://github.com/pixelfed/pixelfed/issues/1710 we should uncomment
|
||||||
log.Info("Id mismatch between Follow request and Accept")
|
// hash is the _ from above
|
||||||
return
|
|
||||||
}
|
// if hash != id {
|
||||||
|
// log.Info("Id mismatch between Follow request and Accept")
|
||||||
|
// return
|
||||||
|
// }
|
||||||
actor.following[acceptor] = hash
|
actor.following[acceptor] = hash
|
||||||
|
delete(actor.requested, acceptor)
|
||||||
actor.save()
|
actor.save()
|
||||||
case "Reject":
|
case "Reject":
|
||||||
rejector := activity["actor"].(string)
|
rejector := activity["actor"].(string)
|
||||||
|
@ -245,6 +258,13 @@ func Serve() {
|
||||||
// we won't try following them again
|
// we won't try following them again
|
||||||
actor.rejected[rejector] = ""
|
actor.rejected[rejector] = ""
|
||||||
actor.save()
|
actor.save()
|
||||||
|
case "Create":
|
||||||
|
actor, ok := actors[mux.Vars(r)["actor"]] // load the actor from memory
|
||||||
|
if !ok {
|
||||||
|
log.Error("No such actor")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
actor.OnReceiveContent(activity)
|
||||||
default:
|
default:
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,6 +85,7 @@ func get(iri string) (info map[string]interface{}, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Info("something went wrong when unmarshalling the json")
|
log.Info("something went wrong when unmarshalling the json")
|
||||||
log.Info(err)
|
log.Info(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
info = e.(map[string]interface{})
|
info = e.(map[string]interface{})
|
||||||
|
|
||||||
|
|
4
setup.go
4
setup.go
|
@ -21,7 +21,7 @@ const version = "0.99"
|
||||||
var client = http.Client{}
|
var client = http.Client{}
|
||||||
|
|
||||||
// Setup sets our environment up
|
// Setup sets our environment up
|
||||||
func Setup(configurationFile string, debug bool) {
|
func Setup(configurationFile string, debug bool) *ini.File {
|
||||||
// read configuration file (config.ini)
|
// read configuration file (config.ini)
|
||||||
|
|
||||||
if configurationFile == "" {
|
if configurationFile == "" {
|
||||||
|
@ -70,6 +70,8 @@ func Setup(configurationFile string, debug bool) {
|
||||||
log.EnableLevel("info")
|
log.EnableLevel("info")
|
||||||
printer.EnableLevel("info")
|
printer.EnableLevel("info")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupStorage creates storage
|
// SetupStorage creates storage
|
||||||
|
|
Loading…
Reference in a new issue