Init
This commit is contained in:
commit
23f8e8aaad
|
@ -0,0 +1,5 @@
|
|||
storage
|
||||
jsonpub
|
||||
.idea
|
||||
private.pem
|
||||
public.pem
|
|
@ -0,0 +1,12 @@
|
|||
FROM golang:1.13-alpine as build
|
||||
ADD . /app
|
||||
WORKDIR /app
|
||||
RUN go build
|
||||
|
||||
FROM alpine:3.11
|
||||
RUN apk add --no-cache tzdata ca-certificates
|
||||
COPY --from=build /app/jsonpub /bin/
|
||||
WORKDIR /app
|
||||
VOLUME /app/storage
|
||||
EXPOSE 8081
|
||||
CMD ["jsonpub"]
|
|
@ -0,0 +1,250 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/dchest/uniuri"
|
||||
"github.com/go-fed/httpsig"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Actor struct {
|
||||
Name, iri, feed string
|
||||
followers map[string]interface{}
|
||||
sentItems map[string]bool
|
||||
privateKey crypto.PrivateKey
|
||||
publicKeyID string
|
||||
}
|
||||
|
||||
type ActorToSave struct {
|
||||
Name string
|
||||
Followers map[string]interface{}
|
||||
SentItems map[string]bool
|
||||
}
|
||||
|
||||
func GetActor(name, iri, feed, pk string) (Actor, error) {
|
||||
// make sure users can't read hard drive
|
||||
if strings.ContainsAny(name, "./ ") {
|
||||
return Actor{}, errors.New("illegal characters in actor name")
|
||||
}
|
||||
// populate actor
|
||||
pkfile, err := ioutil.ReadFile(pk)
|
||||
if err != nil {
|
||||
return Actor{}, errors.New("failed to read private key file")
|
||||
}
|
||||
privateKeyDecoded, rest := pem.Decode(pkfile)
|
||||
if privateKeyDecoded == nil {
|
||||
fmt.Println(rest)
|
||||
return Actor{}, errors.New("failed to decode the private key PEM")
|
||||
}
|
||||
privateKey, err := x509.ParsePKCS1PrivateKey(privateKeyDecoded.Bytes)
|
||||
if err != nil {
|
||||
return Actor{}, errors.New("failed to parse the private key")
|
||||
}
|
||||
actor := Actor{
|
||||
Name: name,
|
||||
iri: iri,
|
||||
feed: feed,
|
||||
followers: make(map[string]interface{}),
|
||||
sentItems: make(map[string]bool),
|
||||
privateKey: privateKey,
|
||||
publicKeyID: iri + "#main-key",
|
||||
}
|
||||
jsonFile := storage + slash + "actors" + slash + name + slash + name + ".json"
|
||||
fileHandle, err := os.Open(jsonFile)
|
||||
if os.IsNotExist(err) {
|
||||
// File doesn't exist, maybe it's a new actor
|
||||
return actor, nil
|
||||
}
|
||||
byteValue, err := ioutil.ReadAll(fileHandle)
|
||||
if err != nil {
|
||||
// Ignore error, but return actor
|
||||
return actor, nil
|
||||
}
|
||||
savedActor := ActorToSave{}
|
||||
err = json.Unmarshal(byteValue, &savedActor)
|
||||
if err != nil {
|
||||
// Ignore error, but return actor
|
||||
return actor, nil
|
||||
}
|
||||
if savedActor.Followers != nil {
|
||||
actor.followers = savedActor.Followers
|
||||
}
|
||||
if savedActor.SentItems != nil {
|
||||
actor.sentItems = savedActor.SentItems
|
||||
}
|
||||
return actor, nil
|
||||
}
|
||||
|
||||
// save the actor to file
|
||||
func (a *Actor) save() error {
|
||||
// check if directory already exists
|
||||
dir := storage + slash + "actors" + slash + a.Name
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
_ = os.MkdirAll(dir, 0755)
|
||||
}
|
||||
actorToSave := ActorToSave{
|
||||
Name: a.Name,
|
||||
Followers: a.followers,
|
||||
SentItems: a.sentItems,
|
||||
}
|
||||
actorJSON, err := json.MarshalIndent(actorToSave, "", "\t")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(storage+slash+"actors"+slash+a.Name+slash+a.Name+".json", actorJSON, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Actor) PostArticle(url string) {
|
||||
create := make(map[string]interface{})
|
||||
note := make(map[string]interface{})
|
||||
create["@context"] = context()
|
||||
create["actor"] = a.iri
|
||||
create["id"] = url
|
||||
create["published"] = note["published"]
|
||||
create["type"] = "Create"
|
||||
article := make(map[string]interface{})
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
req.Header.Add("User-Agent", fmt.Sprintf("%s %s", libName, version))
|
||||
req.Header.Add("Accept", ContentTypeAs2)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil || isSuccess(resp.StatusCode) {
|
||||
fmt.Println("Failed to fetch article")
|
||||
return
|
||||
}
|
||||
respBody, err := ioutil.ReadAll(resp.Body)
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
if err != nil {
|
||||
fmt.Println("Failed to read response from fetched article")
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(respBody, &article)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to unmarshal fetched article")
|
||||
return
|
||||
}
|
||||
create["object"] = article
|
||||
go func() { _ = a.sendToFollowers(create) }()
|
||||
}
|
||||
|
||||
// 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) {
|
||||
b, err := json.Marshal(content)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
postSigner, _, _ := httpsig.NewSigner([]httpsig.Algorithm{httpsig.RSA_SHA256}, "SHA-256", []string{"(request-target)", "date", "host", "digest"}, httpsig.Signature)
|
||||
byteCopy := make([]byte, len(b))
|
||||
copy(byteCopy, b)
|
||||
buf := bytes.NewBuffer(byteCopy)
|
||||
req, err := http.NewRequest(http.MethodPost, to, buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
iri, err := url.Parse(to)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Add("Accept-Charset", "utf-8")
|
||||
req.Header.Add("Date", time.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05")+" GMT")
|
||||
req.Header.Add("User-Agent", fmt.Sprintf("%s %s", libName, version))
|
||||
req.Header.Add("Host", iri.Host)
|
||||
req.Header.Add("Accept", "application/activity+json; charset=utf-8")
|
||||
req.Header.Add("Content-Type", "application/activity+json; charset=utf-8")
|
||||
err = postSigner.SignRequest(a.privateKey, a.publicKeyID, req, byteCopy)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !isSuccess(resp.StatusCode) {
|
||||
err = errors.New("post request failed")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NewFollower records a new follower to the actor file
|
||||
func (a *Actor) NewFollower(iri string, inbox string) error {
|
||||
a.followers[iri] = inbox
|
||||
return a.save()
|
||||
}
|
||||
|
||||
func (a *Actor) RemoveFollower(iri string) error {
|
||||
delete(a.followers, iri)
|
||||
return a.save()
|
||||
}
|
||||
|
||||
// batchSend sends a batch of http posts to a list of recipients
|
||||
func (a *Actor) batchSend(activity map[string]interface{}, recipients []string) (err error) {
|
||||
for _, v := range recipients {
|
||||
_ = a.signedHTTPPost(activity, v)
|
||||
}
|
||||
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) {
|
||||
recipients := make([]string, len(a.followers))
|
||||
i := 0
|
||||
for _, inbox := range a.followers {
|
||||
recipients[i] = inbox.(string)
|
||||
i++
|
||||
}
|
||||
_ = a.batchSend(activity, recipients)
|
||||
return
|
||||
}
|
||||
|
||||
func (a *Actor) newID() (hash string, url string) {
|
||||
hash = uniuri.New()
|
||||
return hash, a.iri + hash
|
||||
}
|
||||
|
||||
// Accept a follow request
|
||||
func (a *Actor) Accept(follow map[string]interface{}) {
|
||||
// it's a follow, write it down
|
||||
newFollower := follow["actor"].(string)
|
||||
// check we aren't following ourselves
|
||||
if newFollower == follow["object"] {
|
||||
// actor and object are equal
|
||||
return
|
||||
}
|
||||
follower, err := NewRemoteActor(follow["actor"].(string))
|
||||
if err != nil {
|
||||
// Couldn't retrieve remote actor info
|
||||
return
|
||||
}
|
||||
// Add or update follower
|
||||
_ = a.NewFollower(newFollower, follower.inbox)
|
||||
// remove @context from the inner activity
|
||||
delete(follow, "@context")
|
||||
accept := make(map[string]interface{})
|
||||
accept["@context"] = "https://www.w3.org/ns/activitystreams"
|
||||
accept["to"] = follow["actor"]
|
||||
_, accept["id"] = a.newID()
|
||||
accept["actor"] = a.iri
|
||||
accept["object"] = follow
|
||||
accept["type"] = "Accept"
|
||||
go func() { _ = a.signedHTTPPost(accept, follower.inbox) }()
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
[general]
|
||||
baseURL = https://jlelse.blog
|
|
@ -0,0 +1,33 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
func allFeedItems(url string) ([]string, error) {
|
||||
jsonFeed := &struct {
|
||||
Items []struct {
|
||||
Url string `json:"url"`
|
||||
} `json:"items"`
|
||||
}{}
|
||||
resp, err := client.Get(url)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to get json feed")
|
||||
}
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to read json feed")
|
||||
}
|
||||
err = json.Unmarshal(body, &jsonFeed)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to parse json feed")
|
||||
}
|
||||
var allUrls []string
|
||||
for _, item := range jsonFeed.Items {
|
||||
allUrls = append(allUrls, item.Url)
|
||||
}
|
||||
return allUrls, nil
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
module codeberg.org/jlelse/jsonpub
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/dchest/uniuri v0.0.0-20160212164326-8902c56451e9
|
||||
github.com/go-fed/httpsig v0.1.1-0.20190924171022-f4c36041199d
|
||||
github.com/gorilla/mux v1.7.3
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 // indirect
|
||||
willnorris.com/go/webmention v0.0.0-20200126231626-5a55fff6bf71
|
||||
)
|
|
@ -0,0 +1,24 @@
|
|||
github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o=
|
||||
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||
github.com/dchest/uniuri v0.0.0-20160212164326-8902c56451e9 h1:74lLNRzvsdIlkTgfDSMuaPjBr4cf6k7pwQQANm/yLKU=
|
||||
github.com/dchest/uniuri v0.0.0-20160212164326-8902c56451e9/go.mod h1:GgB8SF9nRG+GqaDtLcwJZsQFhcogVCJ79j4EdT0c2V4=
|
||||
github.com/go-fed/httpsig v0.1.1-0.20190924171022-f4c36041199d h1:+uoOvOnNDgsYbWtAij4xP6Rgir3eJGjocFPxBJETU/U=
|
||||
github.com/go-fed/httpsig v0.1.1-0.20190924171022-f4c36041199d/go.mod h1:T56HUNYZUQ1AGUzhAYPugZfp36sKApVnGBgKlIY+aIE=
|
||||
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0/go.mod h1:IXCdmsXIht47RaVFLEdVnh1t+pgYtTAhQGj73kz+2DM=
|
||||
golang.org/x/crypto v0.0.0-20180527072434-ab813273cd59 h1:hk3yo72LXLapY9EXVttc3Z1rLOxT9IuAPPX3GpY2+jo=
|
||||
golang.org/x/crypto v0.0.0-20180527072434-ab813273cd59/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3 h1:czFLhve3vsQetD6JOJ8NZZvGQIXlnN3/yXxbT6/awxI=
|
||||
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/sys v0.0.0-20180525142821-c11f84a56e43 h1:PvnWIWTbA7gsEBkKjt0HV9hckYfcqYv8s/ju7ArZ0do=
|
||||
golang.org/x/sys v0.0.0-20180525142821-c11f84a56e43/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
willnorris.com/go/webmention v0.0.0-20200126231626-5a55fff6bf71 h1:F//bgirx4BIsJYHrqAYCZHODn0gSRej/KfueFlarXhs=
|
||||
willnorris.com/go/webmention v0.0.0-20200126231626-5a55fff6bf71/go.mod h1:p+ZRAsZS2pzZ6kX3GKWYurf3WZI2ygj7VbR8NM8qwfM=
|
|
@ -0,0 +1,93 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gorilla/mux"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"willnorris.com/go/webmention"
|
||||
)
|
||||
|
||||
func Serve() {
|
||||
|
||||
webfingerHandler := func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("content-type", "application/jrd+json; charset=utf-8")
|
||||
name := r.URL.Query().Get("resource") // should be something like acct:user@example.com
|
||||
urlBaseUrl, _ := url.Parse(baseURL)
|
||||
re := regexp.MustCompile(`^acct:(.*)@` + regexp.QuoteMeta(urlBaseUrl.Host) + `$`)
|
||||
name = re.ReplaceAllString(name, "$1")
|
||||
actor := actors[name]
|
||||
// error out if this actor does not exist
|
||||
if actor == nil {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
responseMap := make(map[string]interface{})
|
||||
responseMap["subject"] = "acct:" + actor.Name + "@" + urlBaseUrl.Host
|
||||
// links is a json array with a single element
|
||||
var links [1]map[string]string
|
||||
link1 := make(map[string]string)
|
||||
link1["rel"] = "self"
|
||||
link1["type"] = ContentTypeAs2
|
||||
link1["href"] = actor.iri
|
||||
links[0] = link1
|
||||
responseMap["links"] = links
|
||||
response, _ := json.Marshal(responseMap)
|
||||
_, _ = w.Write(response)
|
||||
}
|
||||
|
||||
inboxHandler := func(w http.ResponseWriter, r *http.Request) {
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
activity := make(map[string]interface{})
|
||||
err = json.Unmarshal(b, &activity)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if activity["type"] == "Follow" {
|
||||
actor := actors[mux.Vars(r)["actor"]]
|
||||
// error out if this actor does not exist
|
||||
if actor == nil {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
actor.Accept(activity)
|
||||
} else if activity["type"] == "Undo" {
|
||||
// TODO: Implement unfollow
|
||||
} else if activity["type"] == "Create" {
|
||||
object, ok := activity["object"].(map[string]interface{})
|
||||
if ok {
|
||||
inReplyTo, ok := object["inReplyTo"].(string)
|
||||
id, ok2 := object["id"].(string)
|
||||
if ok && ok2 && len(inReplyTo) > 0 && len(id) > 0 {
|
||||
webmentionClient := webmention.New(&client)
|
||||
endpoint, err := webmentionClient.DiscoverEndpoint(inReplyTo)
|
||||
if err != nil || len(endpoint) < 1 {
|
||||
return
|
||||
}
|
||||
_, err = webmentionClient.SendWebmention(endpoint, id, inReplyTo)
|
||||
if err != nil {
|
||||
log.Println("Sending webmention to " + inReplyTo + " failed")
|
||||
return
|
||||
}
|
||||
log.Println("Sent webmention to " + inReplyTo)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the handlers to a HTTP server
|
||||
gorilla := mux.NewRouter()
|
||||
gorilla.HandleFunc("/.well-known/webfinger", webfingerHandler)
|
||||
gorilla.HandleFunc("/{actor}/inbox", inboxHandler)
|
||||
gorilla.HandleFunc("/{actor}/inbox/", inboxHandler)
|
||||
http.Handle("/", gorilla)
|
||||
|
||||
log.Fatal(http.ListenAndServe(":8081", nil))
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
Setup()
|
||||
SetupActors()
|
||||
go func() {
|
||||
ticker := time.NewTicker(60 * time.Second)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case t := <-ticker.C:
|
||||
fmt.Println("Fetch feeds: ", t.Format(time.RFC3339))
|
||||
for _, actor := range actors {
|
||||
fmt.Println(actor.feed)
|
||||
articles, err := allFeedItems(actor.feed)
|
||||
if err != nil {
|
||||
fmt.Println(actor.feed, err.Error())
|
||||
continue
|
||||
}
|
||||
// Prevent map from getting to big
|
||||
oldSentItems := actor.sentItems
|
||||
actor.sentItems = make(map[string]bool)
|
||||
for _, article := range articles {
|
||||
if oldSentItems[article] == false {
|
||||
fmt.Println("Send", article)
|
||||
go actor.PostArticle(article)
|
||||
}
|
||||
actor.sentItems[article] = true
|
||||
}
|
||||
_ = actor.save()
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
Serve()
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type RemoteActor struct {
|
||||
iri, inbox, sharedInbox string
|
||||
info map[string]interface{}
|
||||
}
|
||||
|
||||
func NewRemoteActor(iri string) (RemoteActor, error) {
|
||||
info, err := get(iri)
|
||||
if err != nil {
|
||||
return RemoteActor{}, err
|
||||
}
|
||||
inbox := info["inbox"].(string)
|
||||
var endpoints map[string]interface{}
|
||||
var sharedInbox string
|
||||
if info["endpoints"] != nil {
|
||||
endpoints = info["endpoints"].(map[string]interface{})
|
||||
if val, ok := endpoints["sharedInbox"]; ok {
|
||||
sharedInbox = val.(string)
|
||||
}
|
||||
}
|
||||
return RemoteActor{
|
||||
iri: iri,
|
||||
inbox: inbox,
|
||||
sharedInbox: sharedInbox,
|
||||
}, err
|
||||
}
|
||||
|
||||
func get(iri string) (info map[string]interface{}, err error) {
|
||||
buf := new(bytes.Buffer)
|
||||
req, err := http.NewRequest("GET", iri, buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
req.Header.Add("Accept", ContentTypeAs2)
|
||||
req.Header.Add("User-Agent", fmt.Sprintf("%s %s", libName, version))
|
||||
req.Header.Add("Accept-Charset", "utf-8")
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
responseData, _ := ioutil.ReadAll(resp.Body)
|
||||
if !isSuccess(resp.StatusCode) {
|
||||
return
|
||||
}
|
||||
var e interface{}
|
||||
err = json.Unmarshal(responseData, &e)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
info = e.(map[string]interface{})
|
||||
return
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var slash = string(os.PathSeparator)
|
||||
var baseURL string
|
||||
var storage = "storage"
|
||||
var actors = make(map[string]*Actor)
|
||||
|
||||
const libName = "jsonpub"
|
||||
const version = "0.0.1"
|
||||
|
||||
var client = http.Client{}
|
||||
|
||||
// Setup sets our environment up
|
||||
func Setup() {
|
||||
// Load base url
|
||||
baseURL = os.Getenv("BASE_URL")
|
||||
if len(baseURL) < 1 {
|
||||
fmt.Printf("BASE_URL not configured")
|
||||
os.Exit(1)
|
||||
}
|
||||
// check if it ends with a / and append one if not
|
||||
if baseURL[len(baseURL)-1:] != "/" {
|
||||
baseURL += "/"
|
||||
}
|
||||
// print baseURL
|
||||
fmt.Println("Base URL:", baseURL)
|
||||
cwd, _ := os.Getwd()
|
||||
fmt.Println("Storage Location:", cwd+slash+storage)
|
||||
}
|
||||
|
||||
// Get actors from env vars
|
||||
// NAMES: names of actors
|
||||
// {{NAME}}_IRI: IRI of actor
|
||||
// {{NAME}}_PK: Storage location of private Key of actor
|
||||
func SetupActors() {
|
||||
namesString := os.Getenv("NAMES")
|
||||
if len(namesString) < 1 {
|
||||
fmt.Printf("NAMES not configured")
|
||||
os.Exit(1)
|
||||
}
|
||||
names := strings.Split(namesString, ",")
|
||||
for _, name := range names {
|
||||
iri := os.Getenv(strings.ToUpper(name) + "_IRI")
|
||||
if len(iri) < 1 {
|
||||
fmt.Printf(strings.ToUpper(name) + "_IRI not configured")
|
||||
os.Exit(1)
|
||||
}
|
||||
feed := os.Getenv(strings.ToUpper(name) + "_FEED")
|
||||
if len(feed) < 1 {
|
||||
fmt.Printf(strings.ToUpper(name) + "_FEED not configured")
|
||||
os.Exit(1)
|
||||
}
|
||||
pk := os.Getenv(strings.ToUpper(name) + "_PK")
|
||||
if len(pk) < 1 {
|
||||
fmt.Printf(strings.ToUpper(name) + "_PK not configured")
|
||||
os.Exit(1)
|
||||
}
|
||||
actor, err := GetActor(name, iri, feed, pk)
|
||||
actors[name] = &actor
|
||||
if err != nil {
|
||||
fmt.Printf(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const ContentTypeAs2 = "application/activity+json"
|
||||
|
||||
func isSuccess(code int) bool {
|
||||
return code == http.StatusOK ||
|
||||
code == http.StatusCreated ||
|
||||
code == http.StatusAccepted ||
|
||||
code == http.StatusNoContent
|
||||
}
|
||||
|
||||
func context() [1]string {
|
||||
return [1]string{"https://www.w3.org/ns/activitystreams"}
|
||||
}
|
Reference in New Issue